Marcos Vainer Loeff

Using PI Web API on HTML5 with jQuery

Blog Post created by Marcos Vainer Loeff Employee on Jun 2, 2014

PI Web API is coming soon and I hope this blog post will help you develop your custom web application taking advantage of the features provided on the latest release of jQuery and HTML5.

 

As you probably know already, PI Web API is a RESTful web service on top of the PI System whose beta version could be downloaded from the vCampus Download Center. In order to clarify some concepts of jQuery and RESTful service, I invite you to read one of my previous blog posts: Calling RESTful services using jQuery.

 

In this blog post you will learn how to develop a HTML5 pages using jQuery showing PI System data without the need of a server-side scripting like PHP or ASP.NET. Please download the source code package by visiting this GitHub repository.

Objectives

 

The sample application is an example about how to use the PI Web API module within your custom HTML5 web pages.  You can download the source code package here.

 

Once the form-data is filled, this application, should:

  • Validate if the PI Data Archive name is correct and if PI Web API is able to connect to it.
  • Validate if the given PI Point name exists on the PI Data Archive
  • Get the snapshot, recorded and interpolated values if the related parameters are properly defined.
  • Show the requested data on an HTML5 page, updating the DOM (Document Object Model) objects with jQuery.

In order to make this work more scalable, a PI Web API module was developed.

 

PI Web API module

 

 

The module is simply a js file extension with 6 public methods available:

  •         SetBaseServiceUrl(string baseUrl) --> This method defined the BaseServiceUrl so that all the other methods could connect to PI Web API. This method should always the the first one to be executed otherwise the other methods will receive an exception.
  •         ValidPIServerName(piServerName) -->Checks if the PI Data Archive name is valid.
  •         ValidPIPointName(piServerName, piPointName) -->Checks if the PI Point name exists within the chosen PI Data Archive.
  •         GetSnapshotValue(piServerName, piPointName) --> Gets the snapshot value  of the selected PI Point.
  •         GetRecordedValues(piServerName, piPointName, startTime, endTime) -->Gets the recorded values  within the chosen start time, end time and PI Point name.
  •         GetInterpolatedValues(piServerName, piPointName, startTime, endTime, interval) -->Gets the interpolated values  within the chosen start time, end time, interval and PI Point name.

Although the methods are able to connect only to the PI Data Archive, you can easily extend them adding new features to connect to the AF Server or to insert new values to in a PI Point using the POST transaction.

 

The complete code of this module is on the file piwebapi_class.js within the source code package under js folder or below:

 

 

 

 

 

var piwebapi = (function () {
    var base_service_url = "NotDefined";

    function GetJsonContent(url, SuccessCallBack, ErrorCallBack) {
        $.ajax({
            type: 'GET',
            url: url,
            cache: false,
            async: true,
            success: SuccessCallBack,
            error: ErrorCallBack

        });
    }

    function CheckPIServerName(piServerName, UpdateDOM) {
        BaseUrlCheck();
        var url = base_service_url + "dataservers?name=" + piServerName;
        GetJsonContent(url, (function (piServerJsonData) {
            UpdateDOM(true);
        }), (function () {
            UpdateDOM(false);
        }));
    }


    function CheckPIPointName(piServerName, piPointName, UpdateDOM) {

        BaseUrlCheck();
        url = base_service_url + "points?path=\\\\" + piServerName + "\\" + piPointName;
        GetJsonContent(url, (function (piPointJsonData) {
            piPointLinksJsonData = piPointJsonData;
            UpdateDOM(true);
        }), (function () {
            UpdateDOM(false)
        }));
    }



    function GetData(piServerName, piPointName, ServiceUrl, QueryString, UpdateDOM) {
        BaseUrlCheck();
        url = base_service_url + "points?path=\\\\" + piServerName + "\\" + piPointName;
        GetJsonContent(url, (function (piPointJsonData) {
            var url_data = piPointJsonData["Links"][ServiceUrl] + QueryString;
            GetJsonContent(url_data, (function (JsonData) {
                UpdateDOM(JsonData);
            }), (function () {
                UpdateDOM("Error: Parameters are incorrect.");
            }));
        }), (function () {
            UpdateDOM("Error: Could not find PI Point on the selected PI Data Archive.");
        }));
    }

    function BaseUrlCheck() {
        if (base_service_url == "NotDefined") {
            alert("Service base url was not defined");
        }
    }

    return {
        ValidPIServerName: function (piServerName, UpdateDOM) {
            CheckPIServerName(piServerName, UpdateDOM)
        },

        ValidPIPointName: function (piServerName, piPointName, UpdateDOM) {
            CheckPIPointName(piServerName, piPointName, UpdateDOM);
        },

        GetSnapshotValue: function (piServerName, piPointName, UpdateDOM) {
            GetData(piServerName, piPointName, "Value", "", UpdateDOM);
        },
        GetRecordedValues: function (piServerName, piPointName, startTime, endTime, UpdateDOM) {
            GetData(piServerName, piPointName, "RecordedData", "?starttime=" + startTime + "&endtime=" + endTime, UpdateDOM);
        },
        GetInterpolatedValues: function (piServerName, piPointName, startTime, endTime, interval, UpdateDOM) {
            GetData(piServerName, piPointName, "InterpolatedData", "?starttime=" + startTime + "&endtime=" + endTime + "&interval=" + interval, UpdateDOM);
        },
        SetBaseServiceUrl: function (baseUrl) {
            base_service_url = baseUrl;
            if (base_service_url.slice(-1) != "/") {
                base_service_url = base_service_url + "/";
            }
        }
    }
}());
  

 

Sample Application

 

 

There are two main HTML5 pages in this application. The first one is pi_data_request.html which is the page responsible for requesting PI data. The user is going to select the PI data archive name, PI Point name, start time, end time, interval and some other options related to data retrieval. Figure 1 shows  the screenshot of the PI data request page on the browser.

 

 

 

5164.sc1.jpg

 

Figure 1 - Screenshot of  PI data request page.

 

 

 

All the text inputs of this page are part of a form. When the button “Get PI Data!” is pressed, the URL is redirected to the page pi_data_result.html appending the form-data into the query string as the method GET is used. One example of URL that shows the result page with the form-data is: http://localhost:36262/pi_data_result.html?piServerName=marc-pi2014&piPointName=SINUSOID&startTime=*-1d&endTime=*&interval=1h&getsnap=yes&getrec=yes&getint=yes.

 

The query string of the URL is read when pi_data_result.html loads, converting it into input variables. This means that the data shown is from the PI Point name stored on the query string. If the method POST is used instead of GET, this information would be stored inside the headers. Therefore, one of the advantages of using GET is that, the end-user would be able to bookmark the pages which he finds interesting. Figure 2 shows the screenshot of the requested data page.

 

 

 

6355.sc2.jpg

 

Figure 2 - Screenshot of  PI data result page.

 

This page takes advantage of the PI Web API module. When the data is received, some jQuery functions and calls are responsible for updating the DOM elements by adding values and timestamps to the appropriate tables, for instance.

 

The code snippet below shows how the application not only checks if the PI Data Archive and PI Point names are valid but also updates the result on the Connection Information table:

 

    piwebapi.ValidPIServerName(piServerName, (function (PIServerExist) { $("#PIServerExistValue").text(PIServerExist); }));
    piwebapi.ValidPIPointName(piServerName, piPointName, (function (PIPointExist) { $("#PIPointExistValue").text(PIPointExist); }));
  

If you are not familiar with jQuery, the last line of code updates the element whose id is "PIPointExistValue" with the content stored on the variable PIPointExist.

 

The other tables are updated also using jQuery methods. Please refer to the source code package.

 

You will realize that the HTML5 markup and the JavaScript/jQuery scripts are in separate files as this is a good practice.  All the JavaScript /jQuery files are within a folder called “js”. Hence, PI Web API module is also located on this folder.

 

If you are interested in developing web apps using AngularJS instead, please refer to this blog post.

Conclusions

 

PI Web API is the PI System access product to integrate the PI System with non .NET  coding languages, which JavaScript is onde of them. I hope that reading this blog post would be a great reference for you to start  developing against jQuery in HTML5 pages.

 

Two of my future blog posts will be about general PHP and how to integrate PI Web API with PHP. Stay tunned!!

Outcomes