Welcome to the 3rd blog post in my "Coresight Candy" series.

 

Hopefully you've already read my first 2 blog posts in this series, but if not then please take a moment to read through them:

Part 1: Project "Coresight Candy" - move over VBA we have a new way to do stuff.

Part 2: Project "Coresight Candy" - part 2 has code in it, and a lot of waffling...

 

Now we are at the point of enriching our PI ProcessBook displays hosted in PI Coresight, and in fact what I'll show you is a method to introduce some event handlers in PI Coresight where we can run some (JQuery) code.

 

First things first.

Before we can consider doing that we first need a method to run our code for each display. Let's solve that part before doing anything fun.

 

The PI Coresight URL for a PBDisplay takes the form of "http://webserver/Coresight/#/PBDisplays/123". You can read a lot into a URL. Instead of the PI ProcessBook display name being part of the URL, the numeric index of the converted display is used instead. We are going to need that index number for this next part.

 

The Files.

We have the following files so far in our project:

  • CoresightCandy.html (the wrapper - just like Jay Z.)
  • CoresightCandy-1.0.0.0.js (the brains - just like the character from Thunderbirds. FAB.)

 

Let's introduce a new fictitious PI ProcessBook display file called "RhysLikesProcessBook.pdi". I'm afraid you cannot change the name of this file otherwise the entire example won't work. Of course I'm kidding, it actually doesn't matter too much as we are really just interested in the index number from PI Coresight.

So let's get that index number by uploading the display to PI Coresight. There we go, our display has been assigned "123" as the index number.

 

That means our "files" (one of them is actually a URL) are now:

  • CoresightCandy.html (the wrapper - just like Jay Z.)
  • CoresightCandy-1.0.0.0.js (the brains - just like the character from Thunderbirds. FAB.)
  • RhysLikesProcessBook.pdi
  • http://webserver/Coresight/#/PBDisplays/123 (the converted display in PI Coresight)

 

Time for a booster shot.

Okay my converted display in PI Coresight is wonderful, it does most of what I want it to do but I need it to do just that little bit more. Time to give my display a booster shot and boost its functionality.

If you remember back to part 2 of Coresight Candy then you shoud remember our 'brains' file has the following code:

$(document).ready(function () {

    var iframe = $('#mainframe'); var iframeURL = $('#mainframeURL');

    iframe.attr('src', 'http://webserver/Coresight/#/PBDisplays/123');
    iframe.attr('height', window.innerHeight);
    iframe.attr('width', window.innerWidth);
    iframe.load(function () {
        // Set up a 1 second timer to check for new navigation in the IFrame
        setInterval(function () {
            var currentLocation = iframe.get(0).contentWindow.location.toString().toLowerCase();
            var currentElement = currentLocation.split("?");

            if (iframeURL.val() !== currentLocation) {
                // Most likely user navigated within the IFrame
                // Inject the Coresight Candy Display specific js

                // Only interested in ProcessBook displays (for now!)
                if (currentLocation.toLowerCase().indexOf('pbdisplays') >= 0) {
                    // Anything that you want to perform on each ProcessBook display
                    // Same could be placed in the Pseudo "Display_Open" event in the Coresight Candy display js


                    // 1. Check for Element Relative Display
                    // 2. Find PBButtons with the prefix "erdlink_"
                    // 3. Add suffix of the current element from the querystring
                    if (currentLocation.indexOf('currentelement=') >= 0) {
                        // Find the anchors
                        var pbButtonLinks = iframe.contents().find('a[*|xlink]').toArray();
                        $.each(pbButtonLinks, function (index, element) { // I've already prefixed the names of the PBButtons with "ERDLINK_" so I know to append the CurrentElement
                            // Make sure it doesn't already have the CurrentElement= set
                            if ($(element).attr('xlink:href').indexOf(currentElement[1]) < 0) {
                                $(element).attr('xlink:href', $(element).attr('xlink:href') + '?' + currentElement[1]);
                            }
                        });
                    }
                }

                // Set the location so we don't continually Inject and overdose
                iframeURL.val(currentLocation);
            }
        }, 1000);
    });
});

$(window).resize(function () {
    $('#mainframe').css('height', window.innerHeight);
    $('#mainframe').css('width', window.innerWidth);
});

 

Anybody that was paying attention may have seen the comment on line 16:

       // Inject the Coresight Candy Display specific js

 

Well this is where we give our display a booster shot. It will mean that everytime that we get a new PI ProcessBook display navigated to from within PI Coresight that we have the opportunity to give it a boost.

For the boost I will need to know two things, the index number of the display and the booster file that I want to give to the display.

 

The index number is straightforward to get because it is in the URL that I mentioned a little bit earlier. However, that is only half the story. We need to load a Javascript/JQuery file for the display to give it a boost. For simplicity of this blog post I am going to just put everything in the root folder of the PI Coresight application.

For the display specific js file I am going to use the naming convention of "CoresightCandy-PBDisplay-[INDEX].js" where "[INDEX]" is replaced with the actual index number from PI Coresight.  For our "RhysLikesProcessBook.pdi" display we got the index number "123" so it's corresponding booster file is going to be "CoresightCandy-PBDisplay-123.js", and it will need to exist in the root of our PI Coresight instance.

 

We'll come to the contents of our booster file shortly. Let's inject it into the display now...

                    // Need the display number when hosted in PI Coresight to match the correct js
                    var pbDisplayNumber = currentElement[0].split('#')[1].replace('/pbdisplays/', '').split('?')[0];
                    var pbScriptRoot = "http://webserver/Coresight/CoresightCandy-PBDisplay-";
                    var pbScriptPath = pbScriptRoot + pbDisplayNumber + ".js";
                    
                    // Inject
                    var script = iframe.get(0).contentWindow.document.createElement('script');
                    script.type = "text/javascript";
                    script.src = pbScriptPath;
                    iframe.get(0).contentWindow.document.body.appendChild(script);

 

Simples. (ComparetheMeerkat.com not ComparetheMarket.com | Compare the Meerkat for anyone who hasn't heard 'Simples' before.)

We get the index number, then form the path to the script file so that we can add it to the display at run time. In order to add it to the display we create a HTML <script> block and add the element to the body of the PI ProcessBook display file currently being viewed in PI Coresight. Job done.

You don't have to worry about your display as all this happens at run time and doesn't affect the stored display.

 

For completeness the 'brains' file now looks like:

$(document).ready(function () {

    var iframe = $('#mainframe'); var iframeURL = $('#mainframeURL');

    iframe.attr('src', 'http://webserver/Coresight/#/PBDisplays/123');
    iframe.attr('height', window.innerHeight);
    iframe.attr('width', window.innerWidth);
    iframe.load(function () {
        // Set up a 1 second timer to check for new navigation in the IFrame
        setInterval(function () {
            var currentLocation = iframe.get(0).contentWindow.location.toString().toLowerCase();
            var currentElement = currentLocation.split("?");

            if (iframeURL.val() !== currentLocation) {
                // Most likely user navigated within the IFrame
                // Inject the Coresight Candy Display specific js
               if (currentLocation.toLowerCase().indexOf('pbdisplays') >= 0) {
                // Need the display number when hosted in PI Coresight to match the correct js
                var pbDisplayNumber = currentElement[0].split('#')[1].replace('/pbdisplays/', '').split('?')[0];
                var pbScriptRoot = "http://webserver/Coresight/CoresightCandy-PBDisplay-";
                var pbScriptPath = pbScriptRoot + pbDisplayNumber + ".js";

                // Inject
                var script = iframe.get(0).contentWindow.document.createElement('script');
                script.type = "text/javascript";
                script.src = pbScriptPath;
                iframe.get(0).contentWindow.document.body.appendChild(script);

                // Only interested in ProcessBook displays (for now!)
                if (currentLocation.toLowerCase().indexOf('pbdisplays') >= 0) {
                    // Anything that you want to perform on each ProcessBook display
                    // Same could be placed in the Pseudo "Display_Open" event in the Coresight Candy display js

                    // 1. Check for Element Relative Display
                    // 2. Find PBButtons with the prefix "erdlink_"
                    // 3. Add suffix of the current element from the querystring
                    if (currentLocation.indexOf('currentelement=') >= 0) {
                        // Find the anchors
                        var pbButtonLinks = iframe.contents().find('a[*|xlink]').toArray();
                        $.each(pbButtonLinks, function (index, element) { // I've already prefixed the names of the PBButtons with "ERDLINK_" so I know to append the CurrentElement
                            // Make sure it doesn't already have the CurrentElement= set
                            if ($(element).attr('xlink:href').indexOf(currentElement[1]) < 0) {
                                $(element).attr('xlink:href', $(element).attr('xlink:href') + '?' + currentElement[1]);
    }
                     
   });
                    }
                }

                // Set the location so we don't continually Inject and overdose
                iframeURL.val(currentLocation);
            }
        }, 1000);
    });
});

$(window).resize(function () {
    $('#mainframe').css('height', window.innerHeight);
    $('#mainframe').css('width', window.innerWidth);
});

 

The booster file.

If you remeber in part 2 of Coresight Candy at the end I showed you the events I wanted to introduce into the PI Coresight display, events from the PI ProcessBook automation model. Well those are the events to put into your display booster file!

 

Let's jump straight into that booster file. In this example that is "CoresightCandy-PBDisplay-123.js":

$(document).ready(function () {
    // Bind click event
    $(document).on('click', 'body', function Display_Click(event) {
        // Check if a new object is selected
        if (event.target != null)
        {
            var xPos = event.pageX; var yPos = event.pageY;
            Display_SelectionChange(event.target);
        }
        else
        {

        }
    });

    // Set up the 5 second timer for Display_DataUpdate()
    setInterval(Display_DataUpdate, 5000);

    // Trigger the Display Open event for initialising stuff
    Display_Open();
});

// ProcessBook equivalent of Display_Open & Display_Activate
function Display_Open() {
    // Add your code here...
};

function Display_DataUpdate() {

};

function Display_SelectionChange(selectedElement) {
    // Add your code here...

};

 

You should recognise that I have pinched the event names from ProcessBook and used them here just for familiarity, they could of course be called absolutely anything you want. However, as I am going to apply a booster file to numerous displays and only targetting ProcessBook displays then it makes sense to use the same names for now.

All we are doing here is:

  • Binding to the "click" event of the body of the HTML produced. Note, all ProcessBook displays have a rectangle background symbol so in PI Coresight you'll always be clicking a symbol, there isn't an equivalent of there being no SelectedSymbols as per ProcessBook.
  • Creating a "Display_SelectionChange" function to handle clicking on different types of symbols. Note, you can also explicitly sign up to click events of particular symbols, but I will be explaining that in part 4 on this blog series.
  • Creating a timer that fires every 5 seconds which calls our "Display_DataUpdate" function...just like in PI ProcessBook.
  • Calling a "Display_Open" function that gets called when PI Coresight has fully displayed the display to the user. This is where in part 4 we'll do some clever stuff to sign up to individual symbol events etc.

 

Time to test it.

To test it you'll need to put all your files in the root of PI Coresight and then use your CoresightCandy.html page to browse to your display. You can hit F12 in your browser to look out for any script errors that you may have introduced by way of typos etc. You should be able to step through the js code at the point where it injects the booster without any issues. If you get this far then great.

However, we haven't actually done anything useful, right? Well here's what you need to do now:

  • Edit "RhysLikesProcessBook.pdi" by adding a "Text" symbol and name it "lblMessage".
  • Save the display and re-upload it to PI Coresight.
  • Open the display in PI Coresight and just check your can see the Text symbol.

 

We now know that our display in PI Coresight, index = 123, has a symbol called "lblMessage" on there. What we'll do now is to prove that our Display_DataUpdate function is being called every 5 seconds.

 

Note: I am going to skip the detail of Symbol definitions in PI Coresight for now. I think that is something that we may want to do as an open collaboration between OSIsoft & the community for Coresight Candy. If there is no desire for that I'll build something at a later date.

 

Let's write out the current time and mention that the DataUpdate event fired. Modify the Display_DataUpdate function to:

function Display_DataUpdate() {
    var lblMessage = $('#lblMessage');
    if (lblMessage.attr('data-symboltype') == '4') { // We have a pbSymbolType.pbSymbolText
        var currentDate = new Date();
        $('#lblMessage_pbTextEl').html(currentDate.getHours() + ':' + currentDate.getMinutes() + ':' + currentDate.getSeconds() + ' DataUpdate event fired');
    }
};

With JQuery we can quickly get the symbol based on it's id (which is set based on the "name" property from ProcessBook).

Then we just make a quick check to ensure we have the equivalent of a Text symbol from ProcessBook, which we find out from the Attribute "data-symboltype" of the HMTL object that should be set to "4".

 

Once we have the symbol then it is time to set our message. Here what we find is that the actual contents of the Text symbol gets put into another HTML object on the display, it is just how it is rendered by PI Coresight. The text is put into a symbol that is suffixed with "_pbTextE1", so it will be "lblMessage_pbTextE1". Note: at a later point in the project we will wrap all this away within an object to simplify interaction with each symbol type.

The symbol actually puts the text content inbetween the HMTL tags (<tag>message goes here</tag>) so that means we need to use the .html() function.

 

Job done. Now test the display via CoresightCandy.html and marvel that your Text symbol is being updated every 5 seconds. View the same display side-by-side in regular PI Coresight (i.e. NOT via CoresightCandy.html) and note that the Text symbol is not updating there!

 

Did somebody sound the alarm?

For this one let's annoy the Coresight Candy users by sounding an "alarm" sound each time they click on the display.

With HTML5 playing a sounds is very straightforward and it has become a built in capability of the browser, so we'll use that capability.

 

First off you'll need your own sound file, which for compatability reasons is best to be an .mp3 file. IE doesn't play .wav files! I just got a simple 1 second alarm.mp3 file that I have put in the root of the PI Coresight app directory. You can use whatever file you like just remember the name for later on. This means I need to reference "http://webserver/Coresight/alarm.mp3".

 

Let me dynamically add the audio link into the display so we can make use of it. For anyone interested in this feature then have a look here for some background: HTML audio tag

As we need the audio tag numerous times we'll add it to the Display_Open method:

// ProcessBook equivalent of Display_Open & Display_Activate
function Display_Open() {
    // Add your code here...
    var alarmAudio = document.createElement('audio');
    alarmAudio.setAttribute('id', 'alarmAudio');
    alarmAudio.setAttribute('src', 'http://webserver/coresight/alarm.mp3');
    document.body.appendChild(alarmAudio);
};

 

Easy peasy. Now to play the sound when the user clicks anywhere on the display. For this I'm using the Display_SelectionChange event:

function Display_SelectionChange(selectedElement) {
    // Add your code here...
    var alarmAudio = $('#alarmAudio');
    alarmAudio[0].play();
};

 

Now watch your users faces as their PI Coresight displays sounds an alarm when they click anywhere on the display!

Okay, that was a bit of fun but hopefully it paints a nice picture for you - you now have the ability to play a sound when a certain action occurs on your display, or when something updates on your display (I'm saving that for part 4 or 5 of this blog).

 

So what next?

I'm currently working on some objects to handle interactions with symbols a little better without needing you as "display enrichers" needing to fully understand the conversion/rendering differences from ProcessBook display to PI Coresight display. Also, I have some "for fun" displays that I am building as an example of how you can use this functionality more extensively.

 

Who knows, perhaps I should look at a solution for having an XY plot in a PI ProcessBook display hosted in PI Coresight?

 

Keep an eye out for part 4 of this blog!

 

Requests!

Okay I now need some feedback from you the community. What functionality do you currently miss from a PI ProcessBook display hosted in PI Coresight? I'm a very competitive person so feel free to throw any kind of challenge at me...and when I say "me" I should really say "us, the community".