Home Support Technical Articles OPC Enabled Performance Counters
Banner
OPC Enabled Performance Counters PDF  | Print |

Making Windows Performance Counters available to OPC

Source Code | OPC Foundation Runtime  | Kepware


Part of building reliable control and SCADA systems is to ensure your IT infrastructure is sound.  Built into the Microsoft Windows platform is a series of performance counters that provide feedback about memory usage, CPU utilization and specific application parameters.   The number of performance counters available will vary from system to system depending on the installed software.  If you building a SCADA package it would be handy for performance information placed within the package, either through alarming or a system screen.  

To solve this we have done a little trick using Kepware Simulation Server (http://www.kepware.com) and a C# application to copy the counter values to OPC.   Kepware’s simulation server is a free (not supported) part of the Kepware package that allows you to define you own server tags without having to build a custom OPC server.  If our application is already using Kepware, all you need to do is ensure that the Simulation Server is installed.  To build the C# application you can use Visual Studio or the Visual Studio express.   To communicate with the OPC services, download the OPC foundations runtime from http://www.opcfoundation.org .

My first step was to author a quick application that would explorer your computer’s list of available performance counters.   This application would allow me to generate a Kepware export CSV file that contains OPC tags that represent my performance counters.   This utility application will also generate a tag configuration file that will be used by my other application that will read the performance counter and write the counter values to Kepware OPC.

Finding Performance Counters Installed

To get started, obtain a list of categories of counters installed on your PC.  This will require the System.Diagnostics namespace to be included in your application.

using System.Diagnostics;


PerformanceCounterCategory[] perfCounters = PerformanceCounterCategory.GetCategories();

Once you have a list of categories of counters, you can then query what types of counters exists.  If your working with the category of LogicalDisk, you may see counter types of  % Free Space C:, Disk Reads/Writes.   To get the list of counters you will need to performanceCounterCategory.GetCounters().

     PerformanceCounter[] counterList = perfCategory.GetCounters();

Before you go calling GetCounters() on all of your categories, you will need to make sure that the category your working with does not have Instances of counters.  If there are no instances of counters contained for the given category, then a simple call to performanceCounterCategory.GetCounters() will be fine.  To determine if there are instances, then you will need to make the call to performanceCounterCategory.GetInstanceNames().  This will return a “string[]” of instance names.  Use each instance name as a parameter to the performanceCounterCategory.GetCounters(name) to find all of the counters.  

Performance Counter to OPC Background Processing

PerfMonToOPCUtil

This utility will allow creating the configuration files required by our PerfMonToOPC process and the Kepware configuration server.  Once you have generated a performance counter list, the counters will need to be converted to tags so they can be imported into Kepware.  Some characters will need to be removed from the counter names for this to work correctly.    For example, if you have Microsoft.NET installed, all of the “.Net * counters” would have trouble loading into Kepware because of the ‘.’ symbol.  In a Kepware import file a dot ‘.’ will represent a new group rather than part of a itemname or group.  To get around this, when I export available counters I replace the ‘.’ with the letters ‘dot’.  

Once my files are created, “kepware.csv” and “perflist.config” for kepware and PerfMonToOPC we are ready to run our application.

 

PerfMonToOPCUtil

When our performance counter utility starts up, we first check to see if the perflist.config file exists.  Next load the performance counter file in to a generic List<T>.  Now that our counter list is ready, we will begin registering each corresponding tag we created in kepware and matching the Opc.Da.ItemValue with a PerformanceCounterDefinition. 

Our main processing loop will connect to the OPC Server, instantiate the performance counters and then finally loop forever while we read performance counter values and write to the OPC server.

Example of our primary thread processing loop:

   public void ThreadProc()   {       

       
// Connect up to our server first
       
       
OPCConnect(PerfMonToOPC.Application.Default.OPCMachineName,            
          
                   PerfMonToOPC.Application.Default.OPCServerClassName);

        // Initalize all the counters       
       
foreach (PerformanceCounterDefinition def in Program._CounterList) {
          def.ActivateCounter();      
       
}

        // Loop for the next tags      
       
while (!_ThreadSignal.WaitOne(2000))   {
          foreach (PerformanceCounterDefinition def in Program._CounterList)   {
             // Grab the next value from the perfmon world
             def.GetNextValue();
          }           // Now that we have refershed the counters, write them to OPC

          WritePerformanceCountersToOPC();
       }
   } 

Activate counter will execute the following code based on the condition if the counter is an instance counter or a single counter.

   if (_Instance == null)       
     
_theCounter = new PerformanceCounter(_Category, _Counter);
  
  
else       
      
_theCounter = new PerformanceCounter(_Category, _Counter, _Instance );

After we loop through all of our performance counters, we will write to the OPC Server with updated values.

     private void WritePerformanceCountersToOPC()        {
         Opc.Da.ItemValue[] itemArray = new Opc.Da.ItemValue[Program._CounterList.Count];

         for (int i = 0; i < Program._CounterList.Count; i++) {
                PerformanceCounterDefinition def = Program._CounterList.ElementAt(i);
                def.OPCItem.Value = (float)def.CounterValue;
                itemArray[i] = def.OPCItem;
         }

        
_OpcServer.Write(itemArray);
     }


One thing to note is the OPCItem is a lazy tagname.  I will not create all of the OPCItem’s prior to entry into the processing loop.  The OPCItem is actually created on the get property of the PerformanceCounterDefinition class.

        private Opc.Da.ItemValue _OPCItem;       
       
public Opc.Da.ItemValue OPCItem
 {           
          
get 
{               
             
if (_OPCItem == null)
 {                   
                 
_OPCItem = new Opc.Da.ItemValue(OPCTagName);
                  _OPCItem.ClientHandle = Guid.NewGuid();
              }
                
              _OPCItem.Value = CounterValue;
                 

              
return _OPCItem;
           
          
}
        }


If the value of _OPCItem is null when a referenced to get the ItemValue, we will create it once dynamically.  We also set the CounterValue to the OPCItem.Value whenever we attempt to reference OPCItem.   All of this will occur within the WritePerformanceCounters function.

                itemArray[i] = def.OPCItem;               


Hopefully this will inspire you to built performance information into your SCADA packages without having to leave the OPC data domain model.   If your using a package like Kepware already for your OPC data infrastructure, this provides a nice extension to your applications.  This same technique can be applied to other OPC packages like OPCSystems.NET or you could roll your own OPC Server using packages like Advosol or Northern Dynamics.