Skip navigation
All Places > PI Developers Club > Blog > 2014 > April

As PI Asset Based Framework 2014 was released this month, I want to take the opportunity to show some features that can be done with this new product programmatically. One of the goals of this first release is to provide users with a rich configuration experience.  Over time, we will provide all the tools needed for users to interact with analyses programmatically.  The purpose of this blog post is to provide you a glimpse of the object and methods currently available within the OSIsoft.AF.Analysis namespace.

Viewing Asset-based analytics properties programmatically



The OSIsoft.AF.Analysis namespace lets you have access programmatically to the asset based analytics where you can have access to its description and plugins settings. You can view which the analysis type, its expressions as well as its scheduling configuration.  In order to make the code snippet below, there must be an analysis properly configured like shown on the following screenshot:






Figure 1 – Asset based analytics settings.


This analysis calculates the average of two different averages calculation Avg1d and Avg2d of the attribute Washington Temperature with two different time ranges. The first variable Avg2d calculates the average value of the last 48 hours and the variable Avg1d store the result of the average calculation from the last 24 hours. The variable SumAvg store the values which are the result of the expression 0.5*Avg1d+0.5*Avg2d. This variable is linked to the Temperature Average which is an Output Attribute.



            PISystems myPISystems = new PISystems();

            //Connect to the AF Server
            PISystem myPISystem = myPISystems["MARC-PI2014"];

            //Connect to the AF Database
            AFDatabase myDb = myPISystem.Databases["AFSDKWorkshop"];
            AFElement Cities = myDb.Elements["Cities"];

            //Store the Washigton element on the Washigton AFEElement object
            AFElement Washington = Cities.Elements["Washington"];

            //Create an AFAttribute linked with Temperature Average of the Washington element
            AFAttribute TempAvg = Washington.Attributes["Temperature Average"];

            //Access the "Washginton Temperature Avg" analyses object
            AFAnalysis WashingtonTempAvg = Washington.Analyses["Washginton Temperature Avg"];
            //Get the analyses description
            Console.WriteLine("\nAnalysis description: " + WashingtonTempAvg.Description);

            //Get the config string of the analyses rule plugin
            Console.WriteLine("\nAnalysis rule plugin config string: " + WashingtonTempAvg.AnalysisRule.ConfigString);
            //Get variable mapping information
            Console.WriteLine("\nVariable mapping information: " + WashingtonTempAvg.AnalysisRule.VariableMapping);

            //Get the config string of the time rule plugin
            Console.WriteLine("\nTime rule config string: " + WashingtonTempAvg.TimeRule.ConfigString);

            //Get the path of analysis
            Console.WriteLine("\nAnalysis path: " + WashingtonTempAvg.GetPath());

            //Get the analysis status
            Console.WriteLine("\nAnalysis status: " + WashingtonTempAvg.Status);

            Console.WriteLine("\nThe script execution has finished. Press any key to quit.");



 This will result in the following console output:






Figure 2 – Console output.





Creating an Asset-based analytic programmatically



The namespace also lets you create the same analysis programmatically. The code snippet below creates the same analysis used on the previous item whose type is Expression:







            PISystems myPISystems = new PISystems();
            PISystem myPISystem = myPISystems["MARC-PI2014"];
            AFDatabase myDb = myPISystem.Databases["AFSDKWorkshop"];
            AFElement Cities = myDb.Elements["Cities"];
            AFElement Washington = Cities.Elements["Washington"];
            AFAttribute TempAvg = Washington.Attributes["Temperature Average"];
            AFAnalysis WashingtonTempAvg = Washington.Analyses["Washginton Temperature Avg"];
            if (WashingtonTempAvg==null)
                WashingtonTempAvg = Washington.Analyses.Add("Washginton Temperature Avg");
            WashingtonTempAvg.Description = "Analysis for Washington Temeperature Average";
            WashingtonTempAvg.AnalysisRulePlugIn = myPISystem.AnalysisRulePlugIns["PerformanceEquation"];
            WashingtonTempAvg.AnalysisRule.ConfigString="Avg2d := TagAvg('Temperature','*-2d','*');\r\nAvg1d := TagAvg('Temperature','*-1d','*');\r\nSumAvg := Avg1d+Avg2d+2;";
            WashingtonTempAvg.TimeRulePlugIn = myPISystem.TimeRulePlugIns["Periodic"];



 This will result on the same analysis shown on Figure 1.




This short blog post shows that the same analysis configured on PI System Explorer could be added, deleted or access programmatically. Nevertheless, the programmatically access is still incomplete since currently you are only able to change the configuration. On future releases,  we plan to release additional methods for public use allowing you to interact with Asset Based Analytics programmatically.  Stay tuned!


PI Web API: Javascript Client

Posted by Bhess Employee Apr 25, 2014

In our last post, we built a simple C# application to get all the Pumps in Wichita out of our database.  Today we're going to do that in Javascript.  If you're following along with my code, simply open the attached HTML file in the text editor of your choice.  If you're starting from scratch, just create a new HTML file with the contents here:

<!DOCTYPE html>
    <title>Wichita Pumps</title>

 Much like last time, the first thing we want to do is add a library that'll help us immensely in the rest of our project.  Within the <head> html element, add a reference to jQuery.  jQuery is a javascript library that makes previously painful Javascript tasks easy. 


Now that we have jQuery available, we can start writing our own scripts.  I've created a couple of <script> tags within my HTML head in order to allow myself to code inline.


One of our first steps is to define some variables to get our URLs set up.  For this purpose, I'm going to include a quick helper method to get myself a .NET-style string format utility in Javascript.  Thanks much to vCampus Team alum Michael van der Veeken for sharing this one with me!  This extends javascript's string type (the 'prototype') to add a format method.

if(!String.prototype.format) {
    String.prototype.format = function() {
        varargs = arguments;
        returnthis.replace(/{(\d+)}/g, function(match, number) {
            returntypeofargs[number] != 'undefined'
              ? args[number]
              : match

 So now I can go ahead and declare some variables within my script tags.  Of course, as always, you'll want to the server and database names to make sense in your own environment:     

var databasePath = "\\\\phlafs04\\nugreen";
var databaseUrl = "{0}".format(databasePath);
var plantSearchUrl;
var pumpSearchUrl;

 Awesome.  Let's make sure this is working correctly.  We'll load up our (currently empty) page in a browser, and check out databaseUrl in the Javascript console.  Look for this in the Developer Tools in your browser (I use Chrome).




 So far, so good.  Now let's use our jQuery library to actually get some data.  Use the jQuery getJSON function to call this URL.  We'll grab the Elements link out of the response, and add our query.

$.getJSON(databaseUrl, function (database) {
    plantSearchUrl = database.Links.Elements + "?templateName=Plant&nameFilter=Wichita&searchFullHierarchy=true";

 Now let's take a look again in the browser:






Oops!  This will happen with a stock PI Web API configuration.  The browser's Same Origin Policy prevents cross-origin requests by default.  PI Web API supports CORS, an industry-standard solution, to work around this problem (see for more information).  PI Web API's CORS support can be adjusted using the PI Web API configuration-- either by making requests against the Web API's /configuration endpoints, or by manually modifying PI Web API's AF configuration database.  You'll want to set "CorsOrigins" to either your hostname, or to "*" to enable CORS traffic from any host.


In this screenshot, "RESTDEMO" is my PI Web API instance name.  Your instance name is usually the same as your hostname.



       $.getJSON(databaseUrl, function (database) {
            plantSearchUrl = database.Links["Elements"] + "?templateName=Plant&nameFilter=Wichita&searchFullHierarchy=true";

            $.getJSON(plantSearchUrl, function (plant) {
                pumpSearchUrl = plant.Items[0].Links["Elements"] + "?templateName=Pump&searchFullHierarchy=true";

                $.getJSON(pumpSearchUrl, function (pumps)
                    var pumpsDomElement = $("#pumps");
                    var jqResults = $(pumps.Items);

                    jqResults.each(function (index, element)
                        pumpsDomElement.append("<li>" + element.Name + "</li>");

 What are we doing here?  In the second callback, we're adding a variable to store a reference to the pumps element in the DOM.  DOM traversal is expensive, so you wouldn't want to make that call for each element in the result list.  Next, we're jQuery-ifying our JSON results, so that we can use the convenient shorthand each function from jQuery, which allows for a function to be executed for each object in the list.  Finally, for each object in the list, we're adding HTML to the DOM node of our list, to have the name of the pump appear as a list item.

Here's our results in the browser!




Thanks for staying tuned.  This concludes our set of basic posts to get you up and running with the PI System Web API in both C# and Javascript.


Find the file here.


PI Web API: C# Client

Posted by Bhess Employee Apr 25, 2014

In my last post we went through the skeleton of how to get all the Pumps in a plant out of our database.  Now let's do that in C# code.  If you're starting from scratch, just create a new C# console application.  I called mine 'WichitaPumps'.  Otherwise, grab the attached download from this post.  To run this solution on your own, you'll want to allow NuGet to download missing packages during build if you haven't already.


The first thing we'll want is to add JSON support to our application.  There's a handy library available via NuGet that's pretty instrumental to doing JSON work in .NET called, vexingly, Json.NET.  It's not a Microsoft product, but it's become such a staple in the .NET community that Microsoft has begun including it as a prerequisite to their own development platforms, like ASP.NET Web API.  So in your own Console application, if you're following along, you'll want to add this reference.  Right click on References, and choose Manage NuGet Packages.  Under Online, and then, search for Json.NET and install it.






Alright, so we have our reference.  The next step we'll want to take is to be able to use this library to do our heavy lifting.  Let's pull some helpful namespaces into scope: 


With these namespaces in scope, we can write ourselves a couple of generic helper method to get a C# dynamic object given a URL: 

        internal static dynamic MakeRequest(string url)
            return MakeRequestAsync(url).Result;

        internal static async Task<dynamic> MakeRequestAsync(string url)
            WebRequest request = WebRequest.Create(url);
            WebResponse response = await request.GetResponseAsync();

            using (StreamReader sw = new StreamReader(response.GetResponseStream()))
                using (JsonTextReader reader = new JsonTextReader(sw))
                    return JObject.ReadFrom(reader);


Note that there are two flavors here:  a synchronous method, and an asynchronous method.  If you're building a multi-threaded application, your main thread won't block while you call the async version, and your user interface will remain responsive.  If your application will run synchronously, then the synchronous call is a simple wrapper around the async call.


So we have these helper methods.  Time to construct our base URL.  Take a look back at our previous post for guidance here on our flow.  Remember that we'll first be looking up the NuGreen database, using its Elements collection to search for a Plant, and then using its Elements collection to search for Pumps.


Our URL from before looked like https://server/piwebapi/assetdatabases?path=path


So let's build that string up in our application.  Be sure to fill in these values appropriately, even if you're just following along with the sample code:  

static string piWebApiServer = "webapiserver";
static string databasePath = @"\\afserver\nugreen";
static string databaseUrlFormat = "https://{0}/piwebapi/assetdatabases?path={1}";
static string databaseUrl = string.Format(databaseUrlFormat, piWebApiServer, databasePath);

 And there we have it.  Now let's try putting it all together!  

static void Main(string[] args)
    dynamic database = MakeRequest(databaseUrl);

Let's take a look at this in the debugger:




Expanding the "Dynamic View" of the database, we see all of its properties in a native C# object.  Great!  We can even expand "Links" to see more:




So let's grab that link to Elements, append our query string from before, and see if we can't locate the Wichita plant. 

string plantQuery = database.Links.Elements + 

dynamic plantResults = MakeRequest(plantQuery);



Remember of course the return format here.  Within the object returned, there's two fields.  The first is an array of query results, called Items.  The second is a dictionary of links, called Links.  Within Items there's a single result.  So let's grab its Elements link for our second query. 

dynamic plant = plantResults.Items[0];



Cool.  So we've successfully retrieved the Wichita plant.   This dynamic stuff is super handy.  We're already ready for the next step.  We want to take the link to Wichita's elements.  Time to build another query string and make another call: 

string pumpQuery = plant.Links.Elements + "?templateName=Pump&searchFullHierarchy=true";
dynamic pumpResults = MakeRequest(pumpQuery);


Okay, we have results!  Great!  The last thing that's left to do is pipe those results to the console. 
foreach(dynamic pump inpumpsResults.Items)
    Console.WriteLine("Pump {0}", pump.Name);

And give our application one more spin:




There we are, grabbing PI System data in a C# application using the PI Web API.  Look forward to a future post on doing the same thing in Javascript!


Find the file here.

PI Web API provides basic functionality needed to retrieve and manipulate Time Series, Asset,  and Event Frame data.  Future releases will incorporate Indexed Search and OData capabilities.  We originally released a CTP in late November 2013; an updated Beta version of the PI Web API is now available in the downloads area here on vCampus. 


I'd like to provide some updated samples for how to retrieve some basic PI data using our latest REST APIs.  This post contains basic information that we think you'll want in order to get started regardless of client platform.  A subsequent post will contain walkthroughs and sample code for doing this programmatically, in both Javascript and C#.


Since all good samples need a story, let's say we're operating in the NuGreen database, and retrieving a listing of all the Pumps in the Houston plant.  For our first stop, let's just navigate to the root of the PI Web API at https://hostname/piwebapi




Thanks to the power of the new HATEOAS ("Hypermedia As The Engine Of Application State") functionality in PI Web API, navigating to resources is now as simple as following links in the product.


Before we dig into retrieving PI data using the Web API, let's first follow the HtmlHelp link.  The online HTML help provides API descriptions for all of the resources that are available in the PI Web API.




The links on the left refer to different groups of resources, or functional areas within the Web API.  The first set of resources is "AssetDatabases".  Since our starting point for our search is the NuGreen database, this is a great place to get started.  My AF server is named phlafs04, and my NuGreen database is named NuGreen, I'll just look it up by path.




Let's follow that link to get a more detailed description of that call:




All of the calls start with https://hostname/piwebapi/ .  What this documentation tells us is that there is a resource named assetdatabases that accepts a query parameter of path.  So, constructing the Web API call is as simple as combining all of that information together using HTTP syntax.  I'm going to call https://hostname/piwebapi/assetdatabases?path=\\server\database




And there it is-- the Web API information related to my NuGreen database.  Notice that the response contains links to other resources-- I can simply follow those links to drill into the database.  Notice that the links all contain long strings of characters that serve as identifiers for the various resources available.  These are referred to as WebID, and will definitely be the topic of a future posting here.  For now, suffice to say that they are simply opaque identifiers that we can use to uniquely identify various components in my PI System.




So we have our NuGreen database.  We'd like to search for Plants within it, in order to find the Houston plant.  One way we could do that is just by drilling into elements by clicking:






If we're going to do this programmatically, there's a more failproof way.  We can add a query to the database's /elements sub-resource.  Again, consulting the HTML help:




We're looking for an Element in the full hierarchy named 'Houston' that implements the 'Plant' template.  So we need to build that up as a query string:




Combine that with the link to the Database's elements from above to get:




And we'll end up with a result that looks like:




Great!  So the last step is to find the Pumps in Houston.  I.e., all the Elements underneath Houston that implement the Pump template.  Our query string will look a lot like the last one:






And there we have it.  All the pumps in Houston.  Stay tuned for our next post, where we'll do this in code.

We are exciting to announce that a beta of the next generation PIBufSS 2014 (v4.3) is now available in the "Pre-Release" area of the vCampus Download Center.  The next generation BufSS sports some significant enhancements and addresses a number of issues commonly affected prior versions.  Some highlights of this beta include:

  • PIbufss now supports buffering to multiple servers and collectives. This is an important feature for clients that rely on PIBufSS to distribute data across a collective. Previously you had to choose one PI collective to buffer to, now you don't have to choose.
  • PIbufss now sends data 2-3 times faster than previous versions (assuming the necessary additional bandwidth is available.) This not only means higher sustained data rates, but it also means that after a network outage any data queued up will be unloaded faster, resulting in users getting live updates that much sooner.
    • New auto-tuning feature attempts to maximize throughput by finding the optimal number of events to send per message
  • PIbufss is now more robust when recovering from an uncontrolled shutdown. When running prior versions it was not uncommon after a power outage for pibufss to stop sending data to the server due to a queue file getting corrupt. This would require manual intervention to dismount and rename/repair the queue. PIbufss 4.3 is built on newer queue technology that can recover from these situations.
  • Improvements for monitoring the health of PIBufSS
    • A single health status that provides indication of the health of PIBufSS
    • A new three tired health diagnostics capability with drilled down capabilities using performance counters
    • New command line options and enhancements
  • New Buffering Manager, which includes:
    • An Upgrade wizard to help migrate from BufServ to PIBufSS 
    • An Add Server wizard to help users through the process of configuring buffering to a new server.
    • A Dashboard for configuring and monitoring buffering. The dashboard can be launched from:
      • ICU (included)
      • SDK Utility (included)
      • PI System Explorer (PI AF Client 2014 - not included)

Note: PI AF 2014 is compatible with PIbufss 2014.


Please consult the release note with additional details and current limitations to this Beta.


As with all non-released software, we do not recommend installing this in a production environment.


We would appreciate your feedback, including bug reports.  Please send them to our dedicated mailbox at:  We will be monitoring this mailbox and will reply back to you as quickly as possible.

We are excited to be one step closer to a more open development environment on PI System! At UC San Francisco we provided more details about future development community and PI System Access technologies. While we are working to iron out final details, these are some highlights that you may be interested to know:

  • We will be opening up licensing and documentation to many of our development technologies soon. This means you will no longer be required to be a paid member of our development program to have the right to develop on these "Open" technologies.
  • We will define different stages of the lifecycle and sensitivity of our extensibility technologies clearly. Everybody will be clearer about what to use and what not to use for their future development projects.
  • Along the same lines of "more hands on the PI System" we are planning to elevate both paid and non-paid levels of our developers community. While we will open up most of the "content" that is currently locked in vCampus (such as forums and blogs) we preserve or add some key benefits to the paid program. Among the exclusive paid benefits are software downloads, Virtual Learning Environment, exclusive previews and betas, premium support, recognition in the community, and member discounts to our technical events and development workshops.
  • To further bring all PI System professionals together we are combining the Users Conference and developer events (TechCon) together starting in 2015. This will help us grow the developers community and facilitate the travel for attendees who like to attend both events.

Stay tuned as we roll out these exciting changes over the coming months! For a more detailed narrative of what future holds for us see our presentation at the Users Conference in San Francisco last week.



Filter Blog

By date: By tag: