Introduction

 

On my previous blog post, I have shown you how to install PHP and how to develop a simple PHP application in Eclipse. On another blog post, we have developed a web application with jQuery/JavaScript which have access to the PI System through PI Web API. This time, I will show you how to develop a similar application not using jQuery/JavaScript but PHP only.

 

What is the different between a web application developed in jQuery and PHP?

 

The main difference is that PHP is a server-side language while jQuery/JavaScript is a client-side language.

 

On the web application with jQuery, when the browser access the web page, the web server sends the HTML pages and the required JavaScript scripts to the browser. The browser is the one who calls the PI Web API which sends the PI System data back to it. The browser then displays the received data to the user in a HTML page.

 

The main disadvantage of this option is security. As the browser is the one who makes the calls to PI Web API, the end user has access to the JavaScript files which contains information concerning which resources are being used by the application including the address of PI Web API and all the GET and POST requests. It does not mean that is not secure but sometimes people want to hide this type of information.

 

We are going to realize that this same web application developed on PHP works a little different. When the browser access the similar page the web server calls the PI Web API which sends the PI System data back to it. Then the web server generates the HTML pages containing the data received and sends to the browser. This way the end user would not have any idea about how and where this data is coming from.

 

It is important to note that PHP is one option for server-side programming. ASP.NET is another alternative to take the benefits of this approach. If you choose ASP.NET, you might consider taking advantage of PI AF SDK which is a .NET library. You cannot use PI AF SDK in PHP applications.

 

PHP Web Application

 

The source code package from this application is available on this GitHub repository. It is very similar to the one developed in jQuery/JavaScript. It actually uses the same Cascate Style Sheet called default.css. There are more two php files in this PHP proejct:

  • Piwebapi_wrapper.php which is the PHP class responsible for connecting and getting data from PI Web API. It does not embed any HTML5 code snippet. There are only static methods used to connect to PI Web API. There is a private method called GetJSONObject() responsable for getting the HTTP response and converting it to a JSON object. Below is the code snippet:

 

<?php
class PIWebAPI
{     
     public static function CheckIfPIServerExists($piServerName)
     {
          $base_service_url = "https://marc-web-sql/piwebapi/";
          $url = $base_service_url . "dataservers";
          $obj = PIWebAPI::GetJSONObject($url);
          foreach($obj->Items as $myServer)
          {
               if(strtolower($myServer->Name)==strtolower($piServerName))
               {
                    return(true);
               }     
          }
          return (false);
     }

     public static function CheckIfPIPointExists($piServerName, $piPointName)
     {
          $base_service_url = "https://marc-web-sql/piwebapi/";
          $url = $base_service_url . "points?path=\\\\" . $piServerName . "\\" . $piPointName;
          $obj1 = PIWebAPI::GetJSONObject($url);
          try {
          if(($obj1->Name)!=null)
          {
               return (true);
          }
          return (false);          
          }
          catch (Exception $e)
          {

          }
     }

     public static function GetSnapshot($piServerName, $piPointName)
     {
          $base_service_url = "https://marc-web-sql/piwebapi/";
          $service_url = $base_service_url . "points?path=\\\\" . $piServerName . "\\" . $piPointName;
          $obj_pipoint = PIWebAPI::GetJSONObject($service_url);
          $url = $obj_pipoint->Links->Value;
          $obj_snapshot = PIWebAPI::GetJSONObject($url);
          return ($obj_snapshot);

     }

     public static function GetRecordedValues($piServerName, $piPointName,$startTime,$endTime)
     {
          $base_service_url = "https://marc-web-sql/piwebapi/";
          $service_url = $base_service_url . "points?path=\\\\" . $piServerName . "\\" . $piPointName;
          $obj_pipoint = PIWebAPI::GetJSONObject($service_url);
          $url = $obj_pipoint->Links->{'RecordedData'} ."?starttime=" . $startTime . "&endtime=" . $endTime;
          $obj_rec = PIWebAPI::GetJSONObject($url);
          return ($obj_rec);

     }

     public static function  GeInterpolatedValues($piServerName, $piPointName,$startTime,$endTime,$interval)
     {
          $base_service_url = "https://marc-web-sql/piwebapi/";
          $service_url = $base_service_url . "points?path=\\\\" . $piServerName . "\\" . $piPointName;
          $obj_pipoint = PIWebAPI::GetJSONObject($service_url);
          $url = $obj_pipoint->Links->{'InterpolatedData'} ."?starttime=" . $startTime . "&endtime=" . $endTime . "&interval=" . $interval;
          $obj_int = PIWebAPI::GetJSONObject($url);
          return ($obj_int);

     }

     private static function GetJSONObject($url)
     {

          $ch = curl_init($url);
          curl_setopt($ch, CURLOPT_HEADER, false);
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
          //header('Content-type: application/json');
          $result = curl_exec($ch);
          $json_o=json_decode($result);
          return ($json_o);
     }     
}

 

 

 

  • Piwebapi_data_retrieval.php is responsible for displaying the HTML pages for the end user. On the web application developed using jQuery there were two HTML pages: pi_data_request.html and pi_data_result.html. The first was used to display the data request form and the second shows the page with PI data if the inputs from the request form were correct. In this case, both pages has been merged to this php file. Below is the code snippet of this file with my comments:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link href="common.css" rel="stylesheet" />
<title>PI Web API and PHP Intergration test</title>
</head>
<body>

<?php
//include_once is used to reference the PHP class responsable for connecting to PI Web API.
include_once ("piwebapi.class.php");


//The browser will make a GET request when called this page for the first time. Therefore isset ( $_POST ["step"] ) will be false.
//When the request is submitted, isset ( $_POST ["step"] ) will be true;

if (isset ( $_POST ["step"] ) and $_POST ["step"] > 0) {

     //We need to get the POST data and store in PHP variables for later use.
     $piServerName = $_POST["piServerName"];
     $piPointName = $_POST["piPointName"];
     $startTime =  $_POST["startTime"];
     $endTime =  $_POST["endTime"];
     $interval =  $_POST["interval"];
     $gsnap =  $_POST["getsnap"];
     $grec =  $_POST["getrec"];
     $gint =  $_POST["getint"];


     //Checking on the PI Web API PHP class if the PI Server name provided is valid.
     $PIServerExists = PIWebAPI::CheckIfPIServerExists ( $piServerName );
     $PIPointExists=false;
     if ($PIServerExists==true)
     {
          //Checking on the PI Web API PHP class if the PI Point name provided is valid.
          $PIPointExists = PIWebAPI::CheckIfPIPointExists ( $piServerName, $piPointName );
     }

     //Calls a PHP method to embed HTML code in response. The same is true for other methods starting with "display"
     //The HTML code is generate according to the input variables of the method (PI Web API response). Therefore, it is a dynamic page.

     displayTitle ( $piPointName );
     displayInfo ( $piServerName, $piPointName, $startTime, $endTime, $interval, $PIServerExists, $PIPointExists );

     if (($PIServerExists==true) && ($PIPointExists==true))
     {
          $SinusoidSnap = PIWebAPI::GetSnapshot ( $piServerName, $piPointName );
          $SinusoidRec = PIWebAPI::GetRecordedValues ( $piServerName, $piPointName, $startTime, $endTime );
          $SinusoidInt = PIWebAPI::GeInterpolatedValues ( $piServerName, $piPointName, $startTime, $endTime, $interval );
         if ($gsnap=="option1")
         {
               displaySnapValues($SinusoidSnap);
         }
         if ($grec=="option1")
         {
              displayRecValues($SinusoidRec);
         }
         if ($gint=="option1")
         {
              displayIntValues($SinusoidInt);
         }
     }

} 
//Display the request form
else {
     displayRequestDataForm ();
}


//When this function is called, PHP will display only the request form.
function displayRequestDataForm() {
     ?>

<h1>Request PI Data</h1>
     <p>Please fill in your details below and click  on "Get PI
          Data". Fields marked with an asterisk (*) are required.</p>
     <form action="piwebapi_data_retrieval.php" method="post">
          <div style='width: 30em;'>

                    <label     for='piServerName'>PI Server Name *</label> 
                    <input type='hidden' name='step' value='1' /> 
                    <input type='text' name='piServerName' id='piServerName' value='' /> <label
                    for='piPointName'>PI Point name *</label> <input type='text'
                    name='piPointName' id='piPointName' value='' /> <label for='startTime'>Start
                    time *</label> <input type='text' name='startTime' id='startTime'
                    value='' /> <label for='endTime'>End time *</label> <input
                    type='text' name='endTime' id='endTime' value='' /> <label
                    for='interval'>Interval *</label> <input type='text' name='interval'
                    id='interval' value='' /> <label for='getsnap'>Get Snapshot?</label>
               <select name='getsnap' id='getsnap' size='1'>
                    <option value='option1'>Yes</option>
                    <option value='option2'>No</option>
               </select> <label for='getrec'>Get Recorded Data?</label> <select
                    name='getrec' id='getrec' size='1'>
                    <option value='option1'>Yes</option>
                    <option value='option2'>No</option>
               </select> <label for='getint'>Get Interpolated Data?</label> <select
                    name='getint' id='getint' size='1'>
                    <option value='option1'>Yes</option>
                    <option value='option2'>No</option>
               </select>

               <div style='clear: both;'>
                    <input type='submit' name='submitButton' id='submitButton' value='Get PI Data!' /> 
                    <input type='button' name='Button' id='DefaultButton' onclick='defaultValues()' value='Default Values' style='margin-right: 20px;' />
               </div>
          </div>
     </form>
        <script>
        function defaultValues() {
            var piServerName = document.getElementById('piServerName');
            var piPointName = document.getElementById('piPointName');
            var StartTime = document.getElementById('startTime');
            var EndTime = document.getElementById('endTime');
            var Interval = document.getElementById('interval');
            piServerName.value = 'MARC-PI2014';
            piPointName.value = 'SINUSOID';
            StartTime.value = '*-1d';
            EndTime.value = '*';
            Interval.value = '1h';
        }
    </script>

               <?php
}
function displayTitle($piPointName) {
     ?>
     <h1>Displaying <?php echo $piPointName?> data</h1>     
               <?php
}
function displayInfo($piServerName, $piPointName, $startTime, $endTime, $interval, $PIServerExists, $PIPointExists) {
     ?>
     <h2>Connection information</h2>
     <br />
     <table style='width: 20em; border: 1px solid #666;'>
          <tr>
               <th>Property</th>
               <th>Value</th>
          </tr>

          <tr>
               <td>PI Server</td>
               <td><?php echo $piServerName ?></td>
          </tr>
          <tr>
               <td>PI Point</td>
               <td><?php echo $piPointName ?></td>
          </tr>
          <tr>
               <td>Start time</td>
               <td><?php echo $startTime ?></td>
          </tr>
          <tr>
               <td>End time</td>
               <td><?php echo $endTime ?></td>
          </tr>
          <tr>
               <td>Interval</td>
               <td><?php echo $interval ?></td>
          </tr>

          <tr>
               <td>Does the PI Server exist?</td>
               <td><?php
     if ($PIServerExists == true) {
          echo 'Yes';
     } else {
          echo 'No';
     }
     ?></td>
          </tr>
          <tr>
               <td>Does the PI Point exist?</td>
               <td><?php

     if ($PIPointExists == true) {
          echo 'Yes';
     } else {
          echo 'No';
     }
     ?></td>
          </tr>
     </table>     
          <?php
}
function displaySnapValues($SinusoidSnap) {
     ?>
     <h2>Snapshot Value of Sinusoid</h2>
     <br />
     <table style='width: 20em; border: 1px solid #666;'>
          <tr>
               <th>Value</th>
               <th>Timestamp</th>
          </tr>
          <tr>
               <td><?php echo $SinusoidSnap->Value; ?></td>
               <td><?php echo $SinusoidSnap->Timestamp; ?></td>
          </tr>
     </table>
     <br />
     <br />
          <?php
}
function displayRecValues($SinusoidRec) {
     ?>

     <h2>Recorded Values of Sinusoid</h2>
     <br />
     <table style='width: 20em; border: 1px solid #666;'>
          <tr>
               <th>Value</th>
               <th>Timestamp</th>
          </tr><?php
     foreach ( $SinusoidRec->Items as $Value ) {
          echo '\n<tr>';
          echo '\n\t<td>' . $Value->Value . '</td>';
          echo '\n\t<td>' . $Value->Timestamp . '</td>';
          echo '\n</tr>';
     }
     ?>

     </table>

     <br />
     <br />
     <?php
}
function displayIntValues($SinusoidInt) {
     ?>
     <h2>Interpolated Values of Sinusoid</h2>
     <br />
     <table style='width: 20em; border: 1px solid #666;'>
          <tr>
               <th>Value</th>
               <th>Timestamp</th>
          </tr>
          <?php
     foreach ( $SinusoidInt->Items as $Value ) {
          echo '\n<tr>';
          echo '\n\t<td>' . $Value->Value . '</td>';
          echo '\n\t<td>' . $Value->Timestamp . '</td>';
          echo '\n</tr>';
     }
     ?>
     </table>
     <?php
}

?>
</body>
</html>

Testing the PHP application

 

As you have realized, piwebapi_data_retrieval.php will display the data request form unless isset( $_POST ["step"]) is equal to true and $_POST ["step"] is higher than 0. When the browser access this file for the first time through the GET request, it will display the request form due to the fact that isset( $_POST ["step"]) would be false. Figure 1 shows the screenshot of data request form.

 

 

 

6708.fig1.png

 

Figure 1 – Data Request Form

 

After filling the values properly, open the developer tools of your browser. In my case I am using Google Chrome. When I click on the button “Get PI Data!”, it appears on the developer tools that a POST request was made to the web server (and not to the PI Web API). Please refer to Figure 2 for more information.

 

 

 

8130.fig2.png

 

Figure 2 – Using Google Chrome Developer Tools to find the POST request of the application.

 

 

 

If you request for more information of this POST request, you will see all the variables sent which will be stored under the php $_POST variable. Please refer to Figure 3 which shows those variables on the Google Chrome Developer Tools.

 

 

 

5543.fig3.png

 

Figure 3 – Using Google Chrome Developer Tools to find the details of the POST request of the application.

 

I hope that with this example you have understood how this PHP program works in general.

 

Conclusion

 

This purpose of this blog post is to provide you an example of a PHP web application on top of the PI Web API.  On the next blog post, we are going to develop a custom data reference which will show the first paragraph of a wikipedia article after searching for a given a name. Stay tuned!