17 Replies Latest reply on May 24, 2011 4:22 AM by Henryk Schneider

    Retrieving PI ACE Process ID or Class/Dll name in code

    Henryk Schneider

      Is it possible to retrieve the Process ID for a given ACE class/dll in code?  Or better still retrieve the class/dll name based on a Process ID?

       

      We have some perfmon points which monitor cpu of specific ACE calculations.  But when you restart the scheduler, the calculations are issued new ID's.    I've got some code finding the Process ID's on the machine via System.Diagnostics library, but need to match that to what is currently in ACE scheduler, so as to hopefully be able to automatically update the respective CPU or other related perfmon points in PI.  

       

      Cheers,

       

      Henryk.

        • Re: Retrieving PI ACE Process ID or Class/Dll name in code

          Good question! I created a new discussion thread with it because it's a new topic, and it will be more easily accessible than if it is buried deep in the other discussion thread.

            • Re: Retrieving PI ACE Process ID or Class/Dll name in code
              Ahmad Fattahi

              To retrieve the process ID that you would see in the task manager you can use the following line in your code:

               

              System.Diagnostics.Process.GetCurrentProcess.Id

               

              Hope it helps!

                • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                  Henryk Schneider

                  Thanks Steve and Ahmad.

                   

                  I was actually hoping to have something outside my current ACE module to investigate the ACE class processes running and their associated ID.

                   

                  I have something that gives me a list of ACE Class processes running:

                   
                  System.Diagnostics.Process.GetProcessesByName(processName, machineName)
                  

                  where processName is "PIACEClassLibraryHost" and machineName is the machine running the ACE calcs.  This works fine, but what I'd like to be able to do is interrogate ACE Scheduler (or wherever stores the Process ID ACE Scheduler knows about) and extract the DLL name and process ID from that. 

                   

                  I need a way to link the actual Dll or ACE calc running with it's Process ID, so I can update the calculation specific Perfmon interface points I have set up in PI, with the correct ID's when the scheduler is restarted.  

                   

                  Or are you saying Ahmad, that I should have some Initialization code in each ACE calc which finds its own Process ID as you suggested and then tries to update the Perfmon Point in PI (if it exists) itself as well?   That would seem a neater solution as it means no other app to manage.  

                   

                  I'm not sure if there is a simpler way to achieve what I'm trying to do, would love to hear it if there is, perhaps that should have been my first question :)  I was just going to write a little app that runs after scheduler is started to update the perfmon interface points in PI.

                   

                   

                   

                  Cheers,

                   

                   

                   

                   

                   

                   

                    • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                      Ahmad Fattahi

                      I will investigate some more to see if there is any easier way to do what you are trying to achieve and update this thread if I find anything. In the meantime, now that I have a better understanding of your problem (thanks to your explanations), I find your solution more sensible. That is because everything would be in one place instead of having the PI tag adjustment code included in every single calculation. in other words, your solution would be more manageable.

                       

                      As a side note, you can find the format of the performance counter path in the PI Performance Monitor Interface user manual where it talks about configuring the PI Points. You can also take advantage of the PIPerfCreator Utility to see some examples.

                        • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                          Henryk Schneider

                          Thanks Ahmad.

                           

                          I've got the perfmon points all sorted I think, I just need to update the Exdesc manually for now, i.e.

                           

                          change the 8300 to new PID after scheduler restarts  ->  \\PIACEA\Process(PIACEClassLibraryHost_8300)\% Processor Time

                           

                          Cheers,
                          H

                          • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                            Ahmad Fattahi

                            Some additional information: When a calculation is started, PI ACE Scheduler updates the ProcessID property under the executable module in PI MDB.  You can query PI MDB after the calculation is successfully started (Status=1).

                             

                            8055.MDB-Snapshot.jpg

                              • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                Henryk Schneider

                                That should do the trick, thanks Ahmad!

                                 

                                Cheers,
                                H

                                  • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                    Ahmad Fattahi

                                    And still one more thing to close the loop here! In case you are interested in automating the other piece of your project as well you can use PI SDK and the methods in the IPIPoints2 interface (such as AddTags and EditTags) to automatically configure and edit tags on your PI Server based on the counter path you configure.

                                     

                                    Hope it helps

                                        • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                          Henryk Schneider

                                          Hi guys, just thought I'd update you on my progress with this.  Seems my prototype is working (thank you Ahmad!), so I thought I'd share the code in case anyone is interested in doing something similar.  

                                           

                                          At the moment my code relies on the Point name to be in a certain format:  
                                          PIDMZ.<ACEserverName>.ACE.<CalcName>.<PerfMonPointType>

                                           

                                          Example:  PIDMZ.PIDEV1.ACE.TransformerCalculations.% User Time

                                           

                                          List of calculation names and perfmon point types I'm storing in a couple application settings arrays (along with a couple other app variables).

                                           

                                           

                                           

                                           

                                           
                                              Public Function UpdatePerfPoints() As Integer
                                          
                                                  Dim PISDK1 = New PISDK.PISDK()
                                                  Dim myServer As PISDK.Server
                                                  Dim sACEServerName As String
                                                  Dim pt As PIPoint
                                                  Dim ptatrs As PointAttributes
                                                  Dim nverrs As NamedValues
                                                  Dim nvsTagAttr As NamedValues
                                                  Dim lProcessID As Long
                                                  Dim sCalc As String
                                                  Dim sPerfMonPoint As String
                                                  Dim sTagAttr As String
                                                  Dim sTagName As String
                                          
                                                  UpdatePerfPoints = 0
                                                  myServer = Nothing
                                          
                                                  Try
                                          
                                                      sACEServerName = My.Settings.PIACEServerName.ToString()
                                                      myServer = PISDK1.Servers.Item(My.Settings.PIServerName.ToString())
                                                      myServer.Open()
                                          
                                                      'If connected ok, then proceed with update.
                                                      If myServer.Connected Then
                                          
                                                          'For each calculation listed in Settings.CalcList array, update the corresponding 
                                                          'ExDesc attribute for each performance point for that calc.
                                                          For Each sCalc In My.Settings.CalcList
                                          
                                                              'Get the new process ID for the current calc (if running).
                                                              If myServer.PIModuleDB.PIModules("%OSI").PIModules("ACEClassLibraries").PIModules(sCalc).PIProperties("Status").Value = 1 Then
                                                                  lProcessID = myServer.PIModuleDB.PIModules("%OSI").PIModules("ACEClassLibraries").PIModules(sCalc).PIProperties("ProcessID").Value
                                          
                                                                  'For each perfmon point in PI for current calc, update the exdesc attribute.
                                                                  For Each sPerfMonPoint In My.Settings.PerfMonPointList
                                          
                                                                      sTagAttr = "\\" & My.Settings.PIACEServerName & "\Process(PIACEClassLibraryHost_" & lProcessID.ToString() & ")\% User Time"
                                                                      sTagName = "PIDMZ." & sACEServerName & ".ACE." & sCalc & "." & sPerfMonPoint
                                          
                                                                      nvsTagAttr = New NamedValues
                                                                      nverrs = New NamedValues
                                          
                                                                      nvsTagAttr.Add("ExDesc", sTagAttr)
                                          
                                                                      ' Set up point and attributes.
                                                                      pt = myServer.PIPoints(sTagName)
                                                                      ptatrs = pt.PointAttributes
                                                                      ptatrs.ReadOnly = False
                                                                      pt.PointAttributes.ModifyAttributes(nvsTagAttr, nverrs)
                                                                      ptatrs.ReadOnly = True
                                          
                                                                  Next
                                          
                                                              End If
                                                          Next
                                          
                                                      Else
                                                          UpdatePerfPoints = -2
                                                      End If
                                          
                                                  Catch ex As Exception
                                          
                                                      MsgBox(ex.Message, MsgBoxStyle.Critical, "Error")
                                                      UpdatePerfPoints = -1
                                          
                                                  Finally
                                          
                                                      If Not IsNothing(myServer) Then
                                                          myServer.Close()
                                                          myServer = Nothing
                                                      End If
                                          
                                                  End Try
                                          
                                              End Function
                                          

                                           

                                           

                                          Cheers,

                                           

                                           

                                            • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                              Ahmad Fattahi

                                              That's awesome Henryk! Thanks for sharing your code and glad it worked out well

                                                • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                  Henryk Schneider

                                                  Just built a little Service based on this prototype and I've noticed a memory leak.  Seem to be the calls to retrieve various Module values, eg:

                                                   
                                                  SomeVariable = myServer.PIModuleDB.PIModules("%OSI").PIModules("ACEClassLibraries").PIModules(sCalc).PIProperties("Status").Value
                                                  

                                                  Even if I set myServer = nothing at the end of it all, the memory is not released.  Been watching the private bytes for the app in PerfMon climbing steadily over time.

                                                   

                                                  Is there a special way one needs to dispose of the server or module object if I was to read the module object first and store it as a local variable before requesting a property value?  

                                                   

                                                  Cheers,
                                                  Henryk. 

                                                    • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                      nagarwal

                                                      Hello Henryk,

                                                       

                                                      It's difficult to comment without looking at the actual code (taking into account that your final code may be different than the prototype you posted). Broadly speaking, what should happen is that when you set the server object (or PIModule etc.) to nothing, the .NET wrapper for that object is marked for garbage collection. When it finally gets garbage collected, the underlying COM object should also be dispose. So it really depends on when the garbage collection really triggers, disposing the underlying COM objects. In my experience, for 64-bit applications, since there is more memory available, garbage collection may not trigger as frequently. Are you running this as a 64-bit application? You could post your code here and we can see if there is something worth pointing out.

                                                       

                                                      Regards,

                                                       

                                                      Nitin

                                                        • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                          Henryk Schneider

                                                          Hi Nitin,

                                                           

                                                          No, just 32bit.  I've got a fair bit of stuff around the core functionality, so I've created a basic test app to try and illustrate the behaviour.  I also thought the GC would do its thing.   Doing a bit more looking around at the various apps we have written here, I discovered that another way to reference a module is to call it up based on its path instead of referencing via the PIServer object.  So my sample test app uses both methods: 1, the way I had done it in my main service, using reference via PIServer object, and 2, getting the module object by path.  I have commented out each at a time and built the exe to run both at the same time and trend the Private Bytes in PerfMon.

                                                           

                                                          By the look of it, getting the module object by path i.e. PIACEBIFunctions.GetPIModuleFromPath("\\PIDEV1\%OSI\ACEClassLibraries") seems much more memory friendly.

                                                           

                                                          After running the test app for about 50min, looks like the GC did do it's job.  I guess the second method might take longer to have its clean up.  I have to run for the day, will report anything else I find in the coming days.  Still amazed at how the memory gets eaten up.  Started off at 20MB and went up to 160MB before getting cleaned up.

                                                           

                                                          Here's my test app code.  Console project consisting of main Module called MODULE1.vb and a class that does the PIModule retrieving called ACECHECK.vb.

                                                           

                                                          MODULE1.vb

                                                           

                                                           

                                                           
                                                          Module Module1
                                                          
                                                          
                                                              Dim tmrMain As New System.Timers.Timer
                                                          
                                                              'Friend WithEvents tmrMain As System.Timers.Timer
                                                          
                                                              Sub Main()
                                                          
                                                                  AddHandler tmrMain.Elapsed, AddressOf tmrMain_Elapsed
                                                                  'tmrMain.AutoReset = True
                                                                  tmrMain.Interval = 2000
                                                                  tmrMain.Enabled = True
                                                                  tmrMain.Start()
                                                                  Console.WriteLine("Running...")
                                                          
                                                                  Console.ReadKey()
                                                          
                                                              End Sub
                                                          
                                                              Private Sub tmrMain_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs)
                                                          
                                                                  Dim ACECheck As New ACECheck
                                                          
                                                                  tmrMain.Enabled = False
                                                                  Console.WriteLine(ACECheck.UpdatePerfPoints())
                                                                  tmrMain.Enabled = True
                                                          
                                                                  ACECheck = Nothing
                                                          
                                                              End Sub
                                                          
                                                          End Module
                                                          

                                                           

                                                           

                                                          ACECheck.vb

                                                           

                                                           

                                                           
                                                          Imports PISDK
                                                          Imports PISDKCommon
                                                          Imports OSIsoft.PI.ACE
                                                          
                                                          Public Class ACECheck
                                                          
                                                              Public Function UpdatePerfPoints() As String
                                                          
                                                                  Dim sRunningScheduler As String = ""
                                                                  Dim sStatus As String = ""
                                                                  Dim piModule As PISDK.PIModule
                                                                  Dim newPISDK As New PISDK.PISDK
                                                                  Dim piServer As PISDK.Server
                                                          
                                                                  piServer = newPISDK.Servers.Item("PIDEV1")
                                                                  piServer.Open()
                                                          
                                                                  If piServer.Connected Then
                                                          
                                                                      '
                                                                      'Method 1:  (Comment out Method 2 below and build)
                                                                      '
                                                                      sRunningScheduler = piServer.PIModuleDB.PIModules("%OSI").PIModules("ACEClassLibraries").PIProperties("Scheduler").Value.ToString()
                                                                      sStatus = piServer.PIModuleDB.PIModules("%OSI").PIModules("ACEClassLibraries").PIProperties("Status").Value.ToString()
                                                          
                                                                      '
                                                                      'Method 2: (Comment out Method 1 above and build)
                                                                      '
                                                                      piModule = PIACEBIFunctions.GetPIModuleFromPath("\\PIDEV1\%OSI\ACEClassLibraries")
                                                                      sRunningScheduler = piModule.PIProperties("Scheduler").Value.ToString()
                                                                      sStatus = piModule.PIProperties("Status").Value.ToString()
                                                          
                                                                      'Set return value
                                                                      UpdatePerfPoints = "Scheduler currently running on " & sRunningScheduler & " with status " & sStatus
                                                                  Else
                                                                      UpdatePerfPoints = "Unable to connect to " & piServer.Name.ToString()
                                                                  End If
                                                          
                                                              End Function
                                                          
                                                          End Class
                                                          

                                                           

                                                           

                                                          Performance Monitor trends.

                                                           

                                                          2335.PIMDBTestPerfMonCapture_5F00_20110504.jpg

                                                           

                                                          Another capture a few minutes later:

                                                           

                                                          2161.PIMDBTestPerfMonCapture_5F00_20110504b.jpg

                                                           

                                                           

                                                           

                                                          Cheers,
                                                          Henryk.

                                                            • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                              Henryk Schneider

                                                              I put the changes into the main application to use the PIACEBIFunctions.GetPIModuleFromPath() method for retrieving the module values.  Using less but memory still increasing over about 3 hours, after which time the GC seemed to do its thing.  But the memory usage by the app climbed about 5 fold in that time.  Keep in mind in the test environment I was calling the code every 10 seconds.

                                                               

                                                              Still not comfortable with it, as I've not seen any of our other apps behave in this way.  

                                                               

                                                              Cheers,
                                                              Henryk. 

                                                                • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                                  nagarwal

                                                                  Hi Henryk,

                                                                   

                                                                  Looking at your test code, one of the problems is that every time the UpdatePerfPoints function is called it creates and destroys the PI SDK hierarchy. This would leave objects marked for garbage collection, only to be claimed back when GC actually triggers. One solution to this problem would be that instead of creating the PIModule objects every time, you should only create them once during initialization (using GetPIModuleFromPath), and then cache for future use. You would have to refresh the objects every time you access the properties, in order to ensure that you get any database changes. For example, if you have already created and cached the PIModule (e.g. in Module1 in your test code), you could do the following:

                                                                   

                                                                  myPIModule = PIACEBIFunctions.GetPIModuleFromPath("\\PIDev1\%OSI\ACEClassLibraries")
                                                                  
                                                                     Public Function UpdatePerfPoints() As String
                                                                  
                                                                        Dim sRunningScheduler As String = ""
                                                                        Dim sStatus As String = ""
                                                                  
                                                                        'Refresh the module before accessing properties 
                                                                        Dim iRefreshPIModule As IRefresh
                                                                        iRefreshPIModule = CType(myPIModule, IRefresh)
                                                                        iRefreshPIModule.Refresh()
                                                                  
                                                                        sRunningScheduler = myPIModule.PIProperties("Scheduler").Value.ToString()
                                                                        sStatus = myPIModule.PIProperties("Status").Value.ToString()
                                                                  
                                                                        'Set return value
                                                                        UpdatePerfPoints = "Scheduler currently running on " & sRunningScheduler & " with status " &sStatus
                                                                     End Function
                                                                  

                                                                   

                                                                   

                                                                  I would not expect memory to grow with this approach. Hope this helps.

                                                                   

                                                                  Thanks

                                                                   

                                                                  Nitin

                                                                    • Re: Retrieving PI ACE Process ID or Class/Dll name in code
                                                                      Henryk Schneider

                                                                      Thanks Nitin, apologies for taking a while to reply, got distracted with a few other jobs.  That proposed solution didn't seem to help in my situation.  In any case, the GC seems to be doing its job, memory is not running away, it just seems to be using more memory than I feel it should for what it does.  Maybe my expectations are unrealistic and that's life with apps in .Net, but moving on, more important things in life to worry about.  

                                                                       

                                                                      The service seems to be working well, had a few maintenance related failovers between the two ACE servers already and the various PerfMon points were automatically updated in PI as desired so overall very happy with it.

                                                                       

                                                                      Thanks for the help.

                                                                       

                                                                      Cheers,
                                                                      H