3 Replies Latest reply on Sep 28, 2012 11:28 PM by nagarwal

    PI ACE Performance Counters at dll level



      Due to the various issues with ACE especially around process management and skipped calculations, I need to

      setup a monitoring system that includes one heartbeat per dll. Based on KB Article# KB00597 - 'PI ACE Performance Monitoring',

      we can setup per-context performance counters in ACE code. A good number of our calculations are done in the way that

      we have one dll that contains more than 1 calculation module and each module have one or more contexts.

      Can we take the performance counters piece of code to the dll level to monitor all contexts under each module?


      Babar Shehzad

        • Re: PI ACE Performance Counters at dll level

          @Babar: PI ACE Calculation .dll file only acts as container of classes. The PI ACE Scheduler will instantiate the calculation class for each context you need and it can track what is related to the class. So, no you cannot monitor performance at the .dll level.

            • Re: PI ACE Performance Counters at dll level

              Thanks Mathieu. I guess at this point, I will create the heartbeats for each dll to handle the process management issues. It is too much of change for us to create performance counters for over 2000 counters.



                • Re: PI ACE Performance Counters at dll level

                  Hi Babar - I am not exactly sure what you are trying to do. But if you just want to add the counters for all contexts (coming from different modules under one executable) you can create a shared class (module in VB .NET) and add counters from different contexts. Note that you would have to synchronize counter increment (e.g. using Interlocked.Increment) since ACE executes contexts in a multi-threaded environment. Have a look at the attached code. 





                  Imports OSIsoft.PI.ACE
                  Imports System.Threading
                  Module ExeLevelPerformanceCounters
                      Private _totalCalculations As Long
                      Private _totalSkippedCalculations As Long
                      Private _totalAbortedCalculations As Long
                      Private _totalErrorCalculations As Long
                      Private _numberOfCalculationsPoint As PIACEPoint = Nothing
                      Private _numberOfSkippedCalculationsPoint As PIACEPoint = Nothing
                      Private _numberOfAbortedCalculationsPoint As PIACEPoint = Nothing
                      Private _numberOfCalculationsWithErrorsPoint As PIACEPoint = Nothing
                      Private _contextToPerformanceCounterData As Dictionary(Of String, PerformanceCounterData) = New Dictionary(Of String, PerformanceCounterData)
                      Private _pointsInitialized As Boolean = False
                      Private _publishingContext As String = "mypiserver\PerformanceCountersExample\ACEModule1\\\mypiserver\Aquariums\Aquarium1"
                      Public Class PerformanceCounterData
                          Public PrevNumberOfCalculations As Long
                          Public PrevNumberOfCalculationsWithError As Long
                          Public PrevNumberOfAbortedCalculations As Long
                          Public PrevNumberOfSkippedCalculations As Long
                      End Class
                      ''' Update performance counter data for the specified calculation.
                      ''' Class for the ACE context.
                      ''' The context specified as the publishing context would also publish performance counters.
                      Public Sub UpdatePerformanceCounters(ByVal context As PIACENetClassModule)
                          Dim data As PerformanceCounterData = Nothing
                          If Not _contextToPerformanceCounterData.TryGetValue(context.Name, data) Then
                              data = New PerformanceCounterData()
                              CopyCounterData(data, context)
                              _contextToPerformanceCounterData.Add(context.Name, data)
                          End If
                          'Calculation delta counters (current value - previous value)
                          Dim deltaCalculations As Long = context.NumberOfCalculations - data.PrevNumberOfCalculations
                          Dim deltaSkippedCalculations As Long = context.NumberOfSkippedCalculations - data.PrevNumberOfSkippedCalculations
                          Dim deltaAbortedCalculations As Long = context.NumberOfAbortedCalculations - data.PrevNumberOfAbortedCalculations
                          Dim deltaCalculationsWithError As Long = context.NumberOfCalculationsWithError - data.PrevNumberOfCalculationsWithError
                          'Increment global/total counters with delta values
                          Interlocked.Add(_totalCalculations, deltaCalculations)
                          Interlocked.Add(_totalSkippedCalculations, deltaSkippedCalculations)
                          Interlocked.Add(_totalAbortedCalculations, deltaAbortedCalculations)
                          Interlocked.Add(_totalErrorCalculations, deltaCalculationsWithError)
                          'Update previous values to current values
                          CopyCounterData(data, context)
                          'Only one context actually writes the performance counter data 
                          If (String.Compare(context.Name, _publishingContext, True) = 0) Then
                          End If
                      End Sub
                      Private Sub PublishPerformanceCounters(ByVal exeTime As Double)
                          If Not _pointsInitialized Then
                          End If
                          SetValue(_numberOfCalculationsPoint, exeTime, _totalCalculations)
                          SetValue(_numberOfSkippedCalculationsPoint, exeTime, _totalSkippedCalculations)
                          SetValue(_numberOfAbortedCalculationsPoint, exeTime, _totalAbortedCalculations)
                          SetValue(_numberOfCalculationsWithErrorsPoint, exeTime, _totalErrorCalculations)
                      End Sub
                      Private Sub InitializePerformanceCounterPoints()
                          Dim serverName As String = "\\mypiserver"
                          _numberOfCalculationsPoint = New PIACEPoint(serverName, "Exe1_NumberOfCalculations", TagAliasUsedType.TagAliasAsOutput)
                          _numberOfSkippedCalculationsPoint = New PIACEPoint(serverName, "Exe1_NumberOfSkippedCalculations", TagAliasUsedType.TagAliasAsOutput)
                          _numberOfAbortedCalculationsPoint = New PIACEPoint(serverName, "Exe1_NumberOfAbortedCalculations", TagAliasUsedType.TagAliasAsOutput)
                          _numberOfCalculationsWithErrorsPoint = New PIACEPoint(serverName, "Exe1_NumberOfCalculationsWithErrors", TagAliasUsedType.TagAliasAsOutput)
                      End Sub
                      Private Sub SetValue(ByVal point As PIACEPoint, ByVal exeTime As Double, ByVal value As Object)
                          point.ExeTime = exeTime
                          point.Value = value
                      End Sub
                      Public Sub CopyCounterData(ByRef counterData As PerformanceCounterData, ByVal context As PIACENetClassModule)
                          counterData.PrevNumberOfCalculations = context.NumberOfCalculations
                          counterData.PrevNumberOfSkippedCalculations = context.NumberOfSkippedCalculations
                          counterData.PrevNumberOfAbortedCalculations = context.NumberOfAbortedCalculations
                          counterData.PrevNumberOfCalculationsWithError = context.NumberOfCalculationsWithError
                      End Sub
                  End Module



                  You would just need to do something like this from each ACECalculations() routine:



                      Public Overrides Sub ACECalculations()
                          'Calculation logic here...
                      End Sub

                   Hope this help. 






                  Note: While the code I posted here should work - you may have to make changes to make sure it works across context restarts etc. You should be able to optimize it as well!