Skip navigation
All Places > PI Developers Club > Blog > Authors pthivierge

PI Developers Club

7 Posts authored by: pthivierge Employee

Best practices with the PI AF SDK were covered many times already, have you missed it? In this post I am providing the resources I find the most useful to learn about the PI AF SDK Best practices.

If you know other valuable resources please let me know in a comment and I will update this table as needed!


General Information

Resource Type
New Features and Best Practices with PI AF SDK 2016 Presentation of the new features that are found in the PI AF SDK 2016 (2.8) as well as an overview on existing best practices with the PI AF SDK.Video (43m) + Presentation ( approx. 38 slides)
Optimizing PI AF Server and PI AF SDK Applications for Performance and ScalabilityThis is a presentation from 2013 (formerly vCampus Live) where Chris Manhard and Paul Combellick went over details in PI AF SDK and the AF Server that can really make a difference when building your application.  This is a must-watch video.Video (60m) + Presentation ( approx. 80 slides)
PI AF SDK GuidelinesThis document highlights aspects of the PI AF SDK that are important to understand.  This is a must-read resource.Whitepaper (34 pages)
Working with the PI AF SDKThe link of this resource points to the content of the Hands On Labs of the UC 2015.  The content of the lab Working with the PI AF SDK gives a very good highlight on scenarios that could benefit from optimization such as lazy loading, scheduled tasks, long running applications, multi-user service.  Note: the code is not available for this resource, but the document and the presentation a really worth to read.Document pdf (16 pages)  + powerpoint (38 slides)



Information Specific to optimization strategies


TitleSummaryResource Type
Performance of Bulks and Parallel callsTake a deep dive into performance testing of bulk calls, the reading of this thread is for advanced users who want to learn in depth details about how bulk calls and parallel calls can help gaining performances.PI Square Discussion

Hello Everyone, today we are presenting to you the beginning of a blog series that will cover the most recent enhancements and features in the PI AF SDK 2016 ( and greater. My colleagues and myself, with the help of the AF SDK Development Team, will prepare and publish those posts in the upcoming weeks. Our initial thought on this is that there are many new features that are worth an explanation; we also believe this may help you in:

  • Learning the best practices when using the AF SDK
  • Understanding .NET features you may not be aware of and don't really know when to use




We already prepared a few topics we want to cover, and you are more than welcome to propose ideas and suggest which topics you would like to be covered first.

Below are the current topics we are preparing to work on. The number is just a reference so you can refer to them in your comments. The order in which the posts will come may differ.


Post Topic
1Why you should use the new AF SDK SearchAF SDK 2016 ( introduces a new and faster search mechanism for elements and event frames. This post will explain how to leverage the new search features.
2Async with PI AF SDK: IntroductionIntroduction to the new async methods that may help improving performances in specific circumstances. We will answer questions such as when should I use async AF SDK calls? And how to use async and await in .NET?
3Async with PI AF SDK: From Tasks to ObservablesMany async use cases involve launching multiple async tasks and processing them as they complete. This post demonstrates how to do so effectively using an Observable pattern provided by Rx.NET. IObservable is the asynchronous complement to IEnumerable found in the (synchronous) AF SDK bulk calls.
4Event frame searching using the AFEventFrameSearch classDiscuss how to go from legacy AF SDK FindEventFrames search criteria to the newer AFEventFrameSearch class.
5New PI Data Archive RPCs - ReplaceValues and RecordedValuesAtTimesLearn about the newly available RPCs on the PI Data Archive and how to leverage them to maximize your application performances.
6New Attributes TraitsAF Attributes may be identified with specific traits or behaviors, such as Limits, Forecast and Analysis Start Triggers to better allow applications to find, display, and analyze related attributes. This post will look into possibilities offered by these new traits from a developer perspective.


Feel free to comment and let us know what topics have the most value for you. Based on your comments we may change / add topics into this list!


We hope you'll enjoy reading and discovering new features


The PI Developers Club Team



Debugging, testing, and understanding what an application does is almost impossible without a good logging system.  When our customers are developing .NET application with PI Developer Technologies we get a lot of questions pointing on a possible bad behavior of our components; further investigations including adding logs in the .NET application often leads to a complete different cause.  This is why I'll share with you how I am implementing a logging system in a .NET application.  You will see this is not difficult and  that a logging system has huge benefits:

  • Helps to understand what happens, and when.
  • Provides an history of actions and errors your application has performed / encountered
  • Speeds up development cycle: having logs also, often, allows to make corrections in the code without having to run the program in debug from Visual Studio, because you can look at log files to determine what is happening, and see when something goes wrong.


This post will explain you how to use Apache log4net in your application


Software used - Requirements

Visual Studio 2015 is used for this post, log4net is compatible with .Net since .Net v1, so if you are using another version of Visual Studio, that should not be a problem

Internet connection to get the NuGet package - You may also reference the .dll directly, NuGet is easier to use though and removes the need of keeping the .dll in your version control system.


Configure your application to use log4net

We'll create a simple application that also contains a library .dll, to see how easy it is to get logs from every parts of your application.

So go ahead and create :

  • a new console application called application.
  • Add a new code library project to the solution and call it library.


1 - Add the Nuget package to your project(s)

From the solution node in Visual Studio, right click and select "Manage NuGet Packages for Solution"


In the NuGet packages manager:

  1. Select Browse (this will search the Package source, on internet)
  2. Enter "log4Net"
  3. Select log4net in the search results
  4. Select all projects in your solution for which you want to use logging
  5. Click Install


It is always good to look at the output to see if there is no error, mine looks like this:


2 - Create the log4net Configuration file

Add an empty .xml file to your application, I like to call it: log4net.config.xml

log4net needs XML configuration to work and this is what makes it so flexible, you can use either: your application.exe.config file or a separate file to store the configuration.  Personally I prefer a separate file and this is how we will configure it in this post.

log4net allows you to configure one or many "appenders".  An Appender is where the data from the logging statements is being written e.g.: AdoNet, MS SQL Server, Console, EventLog, File, Memory, SMTP, etc.

I will provide you the two basic configurations I am using, but keep in mind that this is not limited to it, log4net has a wide range of possibilities, possibly all you can think of you can log into it!

For this post, I'll use the configuration 1 below.


Configuration 1 - For a Console Application

This configuration will show logs in both: the console and a rolling text file:

  • For the console, logs will have a color based on the log level: ALL,WARN,ERROR.

Content of log4net.config.xml:

<log4net debug="false">
  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="Logs\CommandLine.Log" />
    <threshold value="ALL" />
    <appendToFile value="true" />
    <rollingStyle value="Composite" />
    <maximumFileSize value="1MB" />
    <maxSizeRollBackups value="10" />
    <datePattern value="yyyyMMdd" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="*%-10level %-30date %message [%logger] [%thread] %newline" />    </layout>

  <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
      <level value="ERROR" />
      <foreColor value="Red, highintensity" />
      <level value="WARN" />
      <foreColor value="Yellow, highintensity" />
      <level value="ALL" />
      <foreColor value="Green, highintensity" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="*%-10level %-30date %message [%logger] [%thread] %newline" />    </layout>

    <level value="ALL" />
    <appender-ref ref="RollingFile" />
    <appender-ref ref="ColoredConsoleAppender" />



Configuration 2 - For any application

This configuration simply logs into a text file ( for services and any other application type)

Content of log4net.config.xml:

  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="Logs\Application.Log" />
    <threshold value="ALL" />
    <appendToFile value="true" />
    <rollingStyle value="Composite" />
    <maximumFileSize value="1MB" />
    <maxSizeRollBackups value="10" />
    <datePattern value="yyyyMMdd" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="*%-10level %-30date %message [%logger] [%thread] %newline" />      </layout>

    <level value="ALL" />
    <appender-ref ref="RollingFile" />


Now my content in log4net.config.xml is same as what is shown in Configuration 1


One last step is to make sure that the file will be copied when I build the application, so I'll set the file property "Copy to Output Directory" to "Copy if newer":


3- Make the application load log4Net configuration

Open AssemblyInfo.cs

Insert the following line somewhere in the file, it does not matter where you insert it:


[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config.xml", Watch = true)]


<Assembly: log4net.Config.XMLConfigurator(ConfigFile:="Log4Net.Config.xml", Watch:=True)>



The logger variable

Each time you need to log information from a file, you'll need to declare a logger in this file like shown below. Make sure to set the type of your containing class in the typeof operator. (You could hard code the logger type in a string, however, if you rename your class this is best if this is not hard coded.)

Add the following variable in your file, at the class level (or module if VB):


private readonly log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(Program));



Private logger As log4net.Ilog=log4net.LogManager.GetLogger(GetType(Module).ToString)


Full Example

Here is a more concrete example, you should notice the only place I am catching an exception is in the main, if an issue occurs the exception will "bubble up" until there.  Unless you are handling a particular exception, you should not catch exceptions.

application - Program.cs

using System;
using System.Diagnostics;

namespace application
    class Program
        private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(Program));
        static void Main(string[] args)
            var timer = Stopwatch.StartNew();// starting a timer do show how to log it later
            _logger.Info("application is starting...");
            _logger.Warn("Looks like nothing is really happening in this application");

                // doing something with the library
                var worker=new library.Worker();

            catch (Exception ex)
                _logger.Error("Humm... something went as unexpected", ex);
                _logger.InfoFormat("application completed in {0}ms",timer.ElapsedMilliseconds);
            Console.Write("press a key to terminate");


library - Worker.cs

using System;

namespace library
    public class Worker
        private readonly log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(Worker));
        public void doWork()
            _logger.Info("Worker starting...");
            throw new Exception("This is an exception that occurred to show how to log your exceptions with log4Net");


Result when running the application:


Console logs:

The log file in the application\logs directory:

What information are available in the logs

From the log output we could gather a lot of information:

  1. The type of log message, it is up to the developer (you!) to decide what log level to use when logging a message.  In the configuration file (root:level value) you can set which level of logs to output : ALL,DEBUG,INFO,WARN,ERROR,FATAL,OFF e.g.  If you set INFO in the configuration file, you will no see debug logs in the log output.
  2. The precise time of all log events
  3. The log message
  4. The object/class that generated the log. This is really helpful to know where to look in the code to find the entity that logged the message.
  5. The thread id, very useful when debugging multi-threaded applications. FYI: I never had any issue with log4net used by multiple threads.
  6. The full stack trace in case of an error, probably the most useful thing.  As log4net takes the Exception object , as you can see on line 25 of Program.cs, it is really easy to do and really useful.



I hope this helps you to configure logging in your applications, I use log4net all the time in all my applications and I am recommending it strongly.  It is really mature and highly configurable:  you can configure it to write logs in many different places, change the directory or the log file name from the configuration file, etc. I have attached the solution to the post in case you want to see it, you'll need to restore the NuGet packages for it to work.


I am also looking forward for your comments!


Talk to you soon

Hello Everyone,


Today I am presenting you a technical solution to query PI Web API data from Excel.  I was thinking of this for a while now and I think this may be very valuable for our community because:

  • I know you love Excel , and I am also quite certain we will still see VBA around for a while, until everything becomes Javascript maybe? (you should start to learn it if you have not started yet!).
  • It brings access to AF from VBA via PI Web API, something that up to now could only be done using a COM Wrapper (for Processbook VBA or Excel VBA).
  • It also brings a solution to avoid using the PI SDK, even though this is still a good piece of technology, we have to prepare and detach ourselves a little bit from it...


What to expect?

At this time, I am just showing you the proof of concept, it covers having everything setup to make API calls and get the results.  It is to be expected that this will require some work to parse the results returned by the API.

I'd like to hear from you if you would like it to be a community project, a library that we could build together, I am up to it.


Library used

I am using an Excellent library called VBA-Web.  It is well documented and made to work with complex web services.

GitHub Repository and Download    |     Documentation



The library seems quite mature and has many authenticators available.

I have used the Windows Authenticator in my environment and it worked well.  I am not yet certain though that this Windows Authenticator will work in all situations as it seems to be limited to NTLM.

Please let me know if you find that this does not work for you I will update the content in this section.


Depending on your installation, PI Web API can be configured for :

  • Basic Authentication
  • Windows Authentication (Kerberos)
  • Anonymous Authentication (basically, no security.)


So this mean you may need to select a different Authenticator, they are available here.


Step by Step Approach

Setting up the Excel File

  • First, you'll need to download the VBA-Web .zip file, available on GitHub.
  • Unzip the content, and copy the file "VBA-Web - Blank.xlsm" to a working directory.

  • Rename the file to something more meaningful to your intention, e.g. PIAPIWeb, AFStats, etc.
  • Open the file
  • Copy the content of the Authenticator from GitHub, here. (The rest of these steps are assuming the usage of the Windows Authenticator).  In GitHub, Click on the Raw view to copy the content easily.
  • Create a new text file on your computer, called WindowsAuthenticator.cls
  • Paste the content into the file and save the file.
  • Open the VBA Editor in Excel (Alt+F11) and navigate to Class Modules.
  • Right Click on "Class Modules" then select import file.  Then Select the file WindowsAuthenticator.cls you just created


Creating the module to get data from the PI Web API

Ok, so now we are ready to write some code for our own purpose: PI Web API.

  • Create a new Module in the Project, I called it ModPIWebAPI, you can give it the name you like.
  • Then copy paste this code into the Module  and change the PI Web API Address on line 2:
' Base URL of the PI Web API Installation - you should configure it to your server
Const BASE_URL As String = "https://megatron/piwebapi/"

Private Client As WebClient

' initializes the Web Client
Private Sub Init()
    ' set authentication to windows authentication
    ' Check the documentation if you need to set a different authentication type.
    Set Authenticator = New WindowsAuthenticator
    Set Client = New WebClient
    Client.baseUrl = BASE_URL
    Set Client.Authenticator = Authenticator
    ' use only for trusted servers, you should need this only for self signed certificates or with your development server
    ' Client.Insecure = True

End Sub

' This is a "generic function to get content from the PI Web API
' it returns a Web Response object
' see details here:
'e.g. makeRequest("assetservers")
'     makeRequest("system/userinfo")
Public Function MakeRequest(resource As String) As WebResponse

    Call Init
    Dim Request As New WebRequest
    Request.Method = WebMethod.HttpGet
    Request.resource = resource

    Dim Response As WebResponse
    ' You should comment the line below after you are done with your development
    ' this is quite useful
    WebHelpers.EnableLogging = True
    Set Response = Client.Execute(Request)
    ' returns the results
    Set MakeRequest = Response

End Function

' This method returns the WebID of an AF Server
'  e.g.
' GetAssetServerWebId("megatron") --> S0glSFglj -OUWvbNrrcijzwgTUVHQVRST04
Public Function GetAssetServerWebId(ByVal name As String) As String

    Dim Response As WebResponse
    Set Response = MakeRequest("assetservers")
    For Each Item In Response.Data("Items")
        If UCase(Item("Name")) = UCase(name) Then
            GetAssetServerWebId = Item("WebId")
        End If
    Next Item
End Function


  • You can call the MakeRequest to Retrieve content from the PI Web API, any call.e.g:
  • GetGetAssetServerWebId implement some logic based on the MakeRequest method and it also shows how to parse the Response.Data property.  It is probably the beggining of more complex calls that you need to make with the PI Web API.  You can use this a a beggining for your own implementation

Client certificate and the insecure option

   Client.Insecure = True

You can use this option if you work in a development environment and you don't have proper certificates.  You need this option set to true when you see a screen like below in your browser when navigating to the PI Web API URL:

It is present in a commend in the Init() function of the above code.


The WebClient provided in the library has a very nice debugging feature.

It is enabled on line 36 of the code above like this:

WebHelpers.EnableLogging = True


Here is an example of the log output after the client made a request - (in the immediate window of the VBA Editor (Ctrl+G))

--> Request - 5:14:29 PM
GET https://megatron/piwebapi/system/userinfo
User-Agent: VBA-Web v4.0.21 (
Content-Type: application/json
Accept: application/json
Content-Length: 0

<-- Response - 5:14:29 PM
200 OK
Date: Fri, 08 Apr 2016 15:14:29 GMT
Content-Length: 165
Content-Type: application/json; charset=utf-8
Server: Microsoft-HTTPAPI/2.0



More Features

Make sure that you have a look at the library documentation, is has many Web Helpers, such as UrlEncode/Decode that can be useful to prepare search queries and many many more.



As you can see, this implementation is very basic at the moment and you will need to work on parsing the results returned from the WebResponse object. In my next post I'll try to figure out how to make it more straightforward.

I also hope that you will like this library as much as me and I also believe that this opens a door to something new!

I am looking forward for your comments to see what you imagine can be done with it.


I attached my work on this post. Very basic as I said


I would like also to give a special thanks to Tim Hall, his VBA-tools library is just amazing!

His work his licensed under MIT License, this one of the most permissive license, you can do pretty much anything with it.


Edit 2016-05-27:

Did you ever try calling PI Web API from VBA within ProcessBook itself?

Raymond Verhoeff 

I just tested with PI ProcessBook and it also works, added the file to this post. Added details about the Insecure option as well that help when certificate is not secure, see Client certificate and the insecure option section above.

In the PI ProcessBook file, I implemented a small logic to retrieve the user info from the PI Web API.  To make it easier to make a quick test.


Hello Everyone,


I am really happy today to introduce you to Clues, and I am hoping that this project will inspire you as much as me.


You may think that Clues is about the game? Well not really, but somewhat related .

A bit of it is certainly about finding clues to understand how the PI AF SDK works.

More seriously Clues stands for: Command Line Utility & ExampleS


Clues is a community project, hosted on GitHub, that provides a code-base to work with the PI AF SDK.


It has three main goals:

  • provide code examples
  • provide examples in a re-usable and useful form, for the day to day work
  • act as a container for quick prototyping



2015-12-01: I invite you to participate and give your idea with the poll: What should Clues to do next? , thanks for your vote


Fork me on GitHub

If you can't wait to see all the technical details, you may go directly to the GitHub Repository:

osisoft/PI-AF-SDK-Clues · GitHub


Why Clues?


Code samples

The development support team writes a lot of code samples, and many of them may be re-used if written in a way that is generic enough. i.g. Connection to PI, Read Tag Value, etc.

We are certain that you are also writing code that could benefit to others right? Clues was created to centralize and make these samples in an easy format,  shareable and available in a command line form.


What if you could do this to test a connection to the PI Data Archive with AF SDK?

clues PIConnect -s PIServer01


You have heard of EventPipes but you could never test them?

clues PIDataPipeListener -p "optimus" -t simulator.random.15,sinusoid


This is now possible with a simple command line utility : clues


Re-Use and Prototyping

Clues contains a template that you can copy and paste into a new class and allows you to create a new command line applet on a snap:

You rename the class, give it a description, add the command line parameters, and you are ready to go to write your code in the Run() method.  For simple examples, this means only a single file to maintain.

So you can say goodbye to all those solution1 and Project1 that you have created from visual studio that are not re-usable! (that tells you something?)


Imagine that we all have our own personal-clues that (we have forked on GitHub) and we use for our personal testings and recurrent tasks.  When one of us comes up with a useful applet, this would make it much easier to share!


Sounds cool, isn't it?


What can I do with Clues now?


Currently Clues command line has the the following options, and based on your comments / contributions, it will evolve.  So I am not expecting that this list will stay in this post but will be kept up to date in the GitHub Readme.


  AFConnect Connects to a PI System (AF)

  AFCreateAttribute Creates an AF attribute on the specified element.
  Supports all standard attributes.

  AFDataPipeListener Illustrates the functionning of the AF Data Pipe, to
  get changes from AFAttributes as changes occurs

  AFElements List and create elements

  AFGetValue To get values from attribute(s) using the Path

  PIConnect Connects to a PI Data Archive Server

  PIConnectSettings This applet allows to change the timeouts of a PI
  Data Archive Connection.

  PIDataPipeListener Illustrates the functionning of the PI Data Pipe, to
  get changes from PI Tags as changes occurs

  PIDelete Deletes data in archive for specified tag(s) and for
  a specific time range.

  PIFindPoints Finds PIPoints based on tag name filter and
  optionally from point source.

  PIGetCurrentValue Reads the most recent value for the specified tag.

  PIGetCurrentValueBulk Reads the most recent value for multiple tags that
  match the search mask.



What will Clues do tomorrow?

Clues will definitely be an extent to Development Support and serve as a container for code that we believe can bring value in our day to day tasks and support scenarios.  And this is another way to say that Clues will do what you will be asking for , and if you feel even more geeky it will do what you will contribute for!


Want to contribute?


GitHub is still quite new for the community, so don't hesitate to create new questions on PI Square about it, this way we can start building knowledge about it.

You can also look at GitHub Guidelines  this should give you the main picture.


We cannot guarantee that all your contributions will be accepted, and it may require some back and forth before what you are proposing gets integrated in the repository, but this will only be for the best of clues


Ah! And in case you are not ready for GitHub yet (this can happen!), you may send the applet text file to, and we will take it from there.  I would not like to prevent any contributions because of technology!



Ready for GitHub?

A quick and easy explanation here:

GitHub Guides: Forking 4 minutes read


In summary:

  • Fork the repository
  • Clone this fork on your dev machine
  • Make the changes, add more code.
  • Commit the changes locally.
  • Push the changes back to your GitHub forked repository. ( in GitHub for Windows, you press the sync button)
  • Make pull request --> the pull request is where you will send back the contribution to our repository, it is optional, if you'd like to keep all the changes only for you, in your repository, this is up to you .  You may ask us to come and peak in yours that would work too!




I am looking forward for your comments and ideas!


Have a nice day,


Happy PI Day 2015!

Posted by pthivierge Employee Mar 14, 2015

At this precise moment, I take the opportunity to wish you an happy PI Day 2015 folks!

This year is exceptional (3/14/15... ).



PI Day 2015.png


A while ago I had to work on a complex PI UFL Configuration.  Because the text stream was issued from a COM port it was very difficult to make a test setup for it, so I had to create a set of files first to be able to prepare my PI-UFL interface's .INI file.

To achieve this, since the machine was Windows 7, it was convenient to use Powershell to extract a sample of the text content generated by the COM port so I could bring the generated files on my test machine and start working on my UFL Configuration.


Here is the powershell script I created that reads data from a COM port for $DelaysSeconds and output the content into a text file.

It is also attached to the post for convenience:


#This script read lines on a com port for X Minutes
# usage:
# - configure the script parameters
# - run it using powershell from the command line
#   to send the results to a text file, redirects the pipeline to a text file like this:
#   type "powershell" + "enter"
#   then type ".\ReadCOMPort.ps1 > c:\temp\COMXPortData.txt" + "enter"
$Parity=[System.IO.Ports.Parity]::None # System.IO.Ports.Parity
$StopBits=[System.IO.Ports.StopBits]::one # System.IO.Ports.StopBits
# END PARAMETERS ---------------------------------

$period = [timespan]::FromSeconds($DelaySeconds)
$port= new-Object System.IO.Ports.SerialPort $PORT,$BAUDRATE,$Parity,$dataBits,$StopBits

# Debug
#Write-Output 'PORT OPENED'
$StartTime = Get-Date

# Gets the data from the com port for the specified interval
while ((Get-Date) - $StartTime -lt $period) { 
  Write-Output $message

#Write-Output 'PORT CLOSED'

Filter Blog

By date: By tag: