mhamel

PI Web API: C# Client

Discussion created by mhamel Employee on Apr 26, 2014
Latest reply on Dec 28, 2015 by Davidp809

This is a copy of the blog of Brad Hess, Sr. Software Developer at OSIsoft

 

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 nuget.org, search for Json.NET and install it.

 

7888.step10.png

 

7024.10a.png

 

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: 

 
1
2
3
usingSystem.Net;
usingNewtonsoft.Json;
usingNewtonsoft.Json.Linq;

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:

 

5342.Screenshot-_2800_15_2900_.png

 

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:

 

6472.Screenshot-_2800_16_2900_.png

 

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 + 
    "?templateName=Plant&nameFilter=Wichita&searchFullHierarchy=true";

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];

6283.Screenshot-_2800_20_2900_.png 

 

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);

4857.Screenshot-_2800_21_2900_.png

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

And give our application one more spin:

 

8311.15.png

 

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.

Outcomes