Bhess

PI Web API Core Services: C# Client

Blog Post created by Bhess Employee on Nov 27, 2013

This content is outdated!!!  Please refer to PI Web API: C# Client for the latest version.

 

In my last post, we went through the skeleton of how to get all the Pumps in Wichita 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:

 

using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

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

 

        internal static async Task<dynamic> MakeRequest(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 this is an async method, so if you're building, for example, a Windows Store application, your main thread won't block while you call this, and your user interface will remain responsive.  If your application will run synchronously, that's fine; an async method can be run synchronously as well-- in fact, that's what we're going to do.

 

So we have this helper method.  Time to construct some URLs.  Take a look back at our previous post for guidance here on our flow.  Remember that we'll first be doing a search for the Wichita Plant, extracting its Element ID, and then using that as the root for a search of Pumps.

 

Our URL from before looked like http://server/piwebapi/pisystems/afserver/databases/afdatabase/elements?nameFilter=Wichita&template=Plant

 

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 webApiServer = "your-server-name";
        static string afServer = "your-af-server-name";
        static string afDatabase = "your-database-name";
        static string plantSearchUrlFormat = "http://{0}/piwebapi/pisystems/{1}/databases/{2}/elements?template=Plant&nameFilter={3}";

        static string wichitaPlantSearchUrl = string.Format(plantSearchUrlFormat, webApiServer, afServer, afDatabase, "Wichita");

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

 

        static void Main(string[] args)
        {
            dynamic searchResults = MakeRequest(wichitaPlantSearchUrl).Result;
        }

Calling .Result on the async method makes it execute synchronously, and wait for the result.  So now we have a dynamic object sitting there that represents the result of our query.  Let's take a look at that guy in the debugger:

 

3010.11.png

 

Remember of course the return format here.  Within the object returned, there's two fields.  The first is a count of results, called ResultCount.  The second is an array of results called Results.  It looks like that array here has one member, and the property we're after is called Id.  So let's take this code a step further and grab that ID.

 

        static void Main(string[] args)
        {
            dynamic searchResults = MakeRequest(wichitaPlantSearchUrl).Result;
            string wichitaId = searchResults.Results[0].Id;
        }

And let's run it through the debugger, just to make sure it works as expected:

 

3021.12.png

 

Awesome!  This dynamic stuff is super handy.  We're already ready for the next step.  We want to take that ID and use that as the search root for Pumps.  Time to add another URL template:

 

        static string constrainedPumpSearchUrlFormat = "http://{0}/piwebapi/pisystems/{1}/databases/{2}/elements?template=Pump&searchRoot={3}";

And within our Main method, let's keep plugging away:

 

        static void Main(string[] args)
        {
            dynamic searchResults = MakeRequest(wichitaPlantSearchUrl).Result;
            string wichitaId = searchResults.Results[0].Id;

            string wichitaPumpsUrl = string.Format(constrainedPumpSearchUrlFormat, webApiServer, afServer, afDatabase, wichitaId);
            dynamic pumpsResults = MakeRequest(wichitaPumpsUrl).Result;
        }

Now let's take a look at our result.

 

6675.14.png

 

Okay, we have results!  Great!  The last thing that's left to do is pipe those results to the console.

 

            foreach (dynamic pump in pumpsResults.Results)
            {
                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 new PI System Web API.  Look forward to a future post on doing the same thing in Javascript!

Attachments

Outcomes