Introduction

From my experience as a vCampus Support Engineer, I have noted that the big majority of the developers of this community are used to Microsoft technology, especially .NET Framework.

 

As time passes by, the need for web applications development increases and therefore, knowing and using JavaScript is becoming really important. For those who already know a little about this language, you have probably realized that it lacks the familiar comforts compared to a C# or Visual Basic class-based object-oriented programming model, not to mention strong typing, interfaces, and namespaces, which we might consider the bread and butter of our development efforts. 

 

TypeScript is a superset of JavaScript that brings to it an additional object-oriented-like syntax familiar to .NET programmers that compiles down into JavaScript that any browser can run today. Besides being a tool to help .NET developers with JavaScript, its mission statement is “JavaScript for application-scale development”. This means that TypeScript’s aims are not only focused at the long-term issues of scalability and maintainability but it also assist with the day-to-day task of writing code.

Objectives

The purpose of this blog post is to convert the sample application from my previous post written in JavaScript and jQuery files (.js extension) in TypeScript files (.ts). The compiler will convert the .ts files extension back to .js. Nevertheless, the quality of your code will improve since it will be reviewed and generated by the compiler. You can download the source code package by clicking here.

 

If you want to learn more about TypeScript, please refer to Official TypeScript Website and the TypeScript on Codeplex, where you will have access to the source code, documentation and licensing, of this product. There is also a section of ongoing discussions in case you want to post a question.

Pre-requisites

 

 

TypeScript is compatible with Visual Studio 2012 and 2013. In case, you have Visual Studio 2013, you just need to install Update 2 or later to use it. In case you are using Visual Studio 2012, you will have to download the power tool which adds TypeScript support to Visual Studio 2012.

Creating a web project with TypeScript

 

 

After installing TypeScript, a new section called TypeScript is available on the left pane when creating a new project. If you select this section, it will appear a new type of project called “HTML Application TypeScript”. Go ahead and create a new project as shown on Figure 1.

 

 

 

3377.sc1.png

 

 Figure 1 - Creating a new HTML Application with TypeScript

 

The project is created with 4 files: index.html, app.css, app.ts and web.config. If you compile, app.js will be created according to the content of app.ts. A great feature of TypeScript in Visual Studio is that you can easily see the content of the TypeScript file and the compiled version in JS format. This is very important for you to understand what the compiler is doing in the background. Nevertheless, in order to use this resource, you must have installed Web Essentials which extends Visual Studio with a lot of new features for web developers. Unfortunately, it is not compatible with the Express version of Visual Studio.

 

If you open the index.html file, you will see that it references the compiled version of the app.ts called app.js. This means that the html files of our sample application do not need to be changed in case the names of the files without the extension are the same. Please refer to the Figure 2.

 

 

 

 

 

4338.sc2.png

 

  Figure 2 - Screenshot of Visual Studio after creating a new "HTML Application with TypeScript" project.

jQuery declaration files

One of the big advantages of using TypeScript is that it provides IntelliSense for the types, function calls, and variables expected by third-party libraries such as jQuery. Nevertheless, the declaration files (.d.ts) must be added to the project and must be referenced as shown on the first line of the code snippet of the new sample application shown on the following section.

 

If you want to download the declaration file of jQuery, run this command from the Packager Manager Console:

 
Install-Package jquery.TypeScript.DefinitelyTyped

The jquery.d.ts will be downloaded and added to your project. 

 


The new sample application


 

Both js files from my previous post (pi_data_result.ts and piwebapi_class.js) are going to be updated by TypesScript using the .ts files as source files. Concerning the piwebapi_class.ts, its content is shown below:

 

 

 
/// <reference path="jquery.d.ts" />

declare var $: JQueryStatic;

module PIWebAPIModule {
    export class PIWebAPI {
        private static baseServiceUrl: string = "NotDefined";
        private static piPointLinksJsonData: any;

        constructor() { }

        static get ServiceUrl(): string {
            return this.baseServiceUrl;
        }
        static set ServiceUrl(value: string) {
            if (value.slice(-1) != "/") {
                this.baseServiceUrl = value + "/";
            }
            else {
                this.baseServiceUrl = value;
            }
        }

        private static baseUrlCheck(): void {
            if (this.baseServiceUrl == "NotDefined") {
                alert("Service base url was not defined");
            }
        }

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

            });
        }

        public static CheckPIServerName(piServerName: string, UpdateDOM: any) {
            this.baseUrlCheck();
            var url: string = this.baseServiceUrl + "dataservers?name=" + piServerName;
            var SuccessCallBack = function (piServerJsonData: any): void { UpdateDOM(true); };
            var ErrorCallBack = function () { UpdateDOM(false); };
            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);
        }

        public static CheckPIPointName(piServerName: string, piPointName: string, UpdateDOM: any) {
            this.baseUrlCheck();
            var url: string = this.baseServiceUrl + "points?path=\\\\" + piServerName + "\\" + piPointName;
            var SuccessCallBack = function (piPointJsonData: any): void { UpdateDOM(true); this.piPointLinksJsonData = piPointJsonData; };
            var ErrorCallBack = function () { UpdateDOM(false); };
            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);
        }

        private static GetData(piServerName: string, piPointName: string, ServiceUrl: string, QueryString: string, UpdateDOM: any) {
            this.baseUrlCheck();
            var url: string = this.baseServiceUrl + "points?path=\\\\" + piServerName + "\\" + piPointName;
            var ErrorCallBackInner = function (): void { UpdateDOM("Error: Parameters are incorrect."); };
            var ErrorCallBackOutter = function (): void { UpdateDOM("Error: Could not find PI Point on the selected PI Data Archive."); };
            var SecondSuccessCallBack = function (JsonData): void { UpdateDOM(JsonData); };
            var FirstSuccessCallBack = (piPointJsonData) => {
                var url_data: any = piPointJsonData["Links"][ServiceUrl] + QueryString;
                this.GetJsonContent(url_data, SecondSuccessCallBack, ErrorCallBackInner);
            }
            this.GetJsonContent(url, FirstSuccessCallBack, ErrorCallBackOutter);
        }

        public static GetSnapshotValue(piServerName: string, piPointName: string, UpdateDOM: any): void {
            this.GetData(piServerName, piPointName, "Value", "", UpdateDOM);
        }

        public static GetRecordedValues(piServerName: string, piPointName: string, startTime: string, endTime: string, UpdateDOM: any): void {
            this.GetData(piServerName, piPointName, "RecordedData", "?starttime=" + startTime + "&endtime=" + endTime, UpdateDOM);
        }

        public static GetInterpolatedValues(piServerName: string, piPointName: string, startTime: string, endTime: string, interval: string, UpdateDOM: any): void {
            this.GetData(piServerName, piPointName, "InterpolatedData", "?starttime=" + startTime + "&endtime=" + endTime + "&interval=" + interval, UpdateDOM);
        }
    }
}

 

 

 

 

There are a few comments I want to point out:

  • The first line is used to refer to the declaration file of jQuery, so that intellisense would work with this third-party library.
  • The purpose of the second line is to define $ as JQueryStatic.
  • Although there is no namespaces as there is on C#, TypeScript uses the concept of module. It is possible to write a whole tree hierarchy of modules with child modules and so on.
  • If you want your class to be accessed outside the module, the class must use the export keyword. 
  • There isn't anything like using which means that references to the class must be fully qualified with module and class name.
  • The public and private keywords on methods are used on the same way as in C#. The same happens with the static keyword.
  • It is possible to define the type of each variable as (piServerName : string). The same is valid for the data type of the returned variable from a function.

 

 

After compiling, the content of the file piwebapi_class.js is.

 

  

 
/// <reference path="jquery.d.ts" />

var PIWebAPIModule;
(function (PIWebAPIModule) {
    var PIWebAPI = (function () {
        function PIWebAPI() {
        }
        Object.defineProperty(PIWebAPI, "ServiceUrl", {
            get: function () {
                return this.baseServiceUrl;
            },
            set: function (value) {
                if (value.slice(-1) != "/") {
                    this.baseServiceUrl = value + "/";
                } else {
                    this.baseServiceUrl = value;
                }
            },
            enumerable: true,
            configurable: true
        });

        PIWebAPI.baseUrlCheck = function () {
            if (this.baseServiceUrl == "NotDefined") {
                alert("Service base url was not defined");
            }
        };

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

        PIWebAPI.CheckPIServerName = function (piServerName, UpdateDOM) {
            this.baseUrlCheck();
            var url = this.baseServiceUrl + "dataservers?name=" + piServerName;
            var SuccessCallBack = function (piServerJsonData) {
                UpdateDOM(true);
            };
            var ErrorCallBack = function () {
                UpdateDOM(false);
            };
            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);
        };

        PIWebAPI.CheckPIPointName = function (piServerName, piPointName, UpdateDOM) {
            this.baseUrlCheck();
            var url = this.baseServiceUrl + "points?path=\\\\" + piServerName + "\\" + piPointName;
            var SuccessCallBack = function (piPointJsonData) {
                UpdateDOM(true);
                this.piPointLinksJsonData = piPointJsonData;
            };
            var ErrorCallBack = function () {
                UpdateDOM(false);
            };
            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);
        };

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

        PIWebAPI.GetSnapshotValue = function (piServerName, piPointName, UpdateDOM) {
            this.GetData(piServerName, piPointName, "Value", "", UpdateDOM);
        };

        PIWebAPI.GetRecordedValues = function (piServerName, piPointName, startTime, endTime, UpdateDOM) {
            this.GetData(piServerName, piPointName, "RecordedData", "?starttime=" + startTime + "&endtime=" + endTime, UpdateDOM);
        };

        PIWebAPI.GetInterpolatedValues = function (piServerName, piPointName, startTime, endTime, interval, UpdateDOM) {
            this.GetData(piServerName, piPointName, "InterpolatedData", "?starttime=" + startTime + "&endtime=" + endTime + "&interval=" + interval, UpdateDOM);
        };
        PIWebAPI.baseServiceUrl = "NotDefined";
        return PIWebAPI;
    })();
    PIWebAPIModule.PIWebAPI = PIWebAPI;
})(PIWebAPIModule || (PIWebAPIModule = {}));
//# sourceMappingURL=piwebapi_class.js.map

 

 


Concerning the pi_data_result.ts file, this file was not changed as much as the piwebapi_class.ts mainly because it was not written as a module.

 

 

 
/// <reference path="piwebapi_class.ts" />
/// <reference path="jquery.d.ts" />

declare var $: JQueryStatic;

function GetQueryStringParams(sParam: string): string {
    var sPageURL: string = window.location.search.substring(1);
    var sURLVariables: string[] = sPageURL.split('&');
    for (var i = 0; i < sURLVariables.length; i++) {
        var sParameterName: string[] = sURLVariables
.split('=');
        if (sParameterName[0] == sParam) {
            return sParameterName[1];
        }
    }
    return "";
}

function AppendErrorToTable(DataObj: string, TableToAdd: string): void {
    var ErrorMsgs: string = DataObj;
    $('<tr/>', {
        'text': ErrorMsgs
    }).appendTo(TableToAdd);
}

function StartRetrievalMethod(PerformRequest: string, RetrievalMethodName: string, TableToAdd: string, RetrievalMethodClass: string, RetrievalMethodData: string): void {

    if (PerformRequest == "yes") {
        try {
            for (var i = 0; i < RetrievalMethodData["Items"].length; i++) {
                $('<tr/>', {
                    'id': RetrievalMethodName + 'Tr' + i,
                }).appendTo(TableToAdd);
                $('<td/>', {
                    'text': RetrievalMethodData["Items"]
.Value
                }).appendTo('#' + RetrievalMethodName + 'Tr' + i);
                $('<td/>', {
                    'text': RetrievalMethodData["Items"]
.Timestamp
                }).appendTo('#' + RetrievalMethodName + 'Tr' + i);
            }
        }
        catch (e) {
            if (RetrievalMethodData["Value"] != undefined) {
                $('<tr/>', {
                    'id': RetrievalMethodName + 'Tr',
                }).appendTo(TableToAdd);
                $('<td/>', {
                    'text': RetrievalMethodData["Value"]
                }).appendTo('#' + RetrievalMethodName + 'Tr');
                $('<td/>', {
                    'text': RetrievalMethodData["Timestamp"]
                }).appendTo('#' + RetrievalMethodName + 'Tr');
            }
            else {
                AppendErrorToTable(RetrievalMethodData, TableToAdd);
            }
        }
        $(RetrievalMethodClass).css("visibility", "visible")
    }
    else {
        $(RetrievalMethodClass).css("visibility", "hidden")
    }
}

$(document).ready( () => {
    var piServerName: string = GetQueryStringParams('piServerName');
    var piPointName: string = GetQueryStringParams('piPointName');
    var startTime: string = GetQueryStringParams('startTime');
    var endTime: string = GetQueryStringParams('endTime');
    var interval: string = GetQueryStringParams('interval');
    var getsnap: string = GetQueryStringParams('getsnap');
    var getrec: string = GetQueryStringParams('getrec');
    var getint: string = GetQueryStringParams('getint');
    $("#PIServerNameValue").text(piServerName);
    $("#PIPointNameValue").text(piPointName);
    $("#StartTimeValue").text(startTime);
    $("#EndTimeValue").text(endTime);
    $("#IntervalValue").text(interval);

    //PIWebAPIModule.PIWebAPI.ServiceUrl ="https://demo.piwebapi.net/piwebapi/";
    PIWebAPIModule.PIWebAPI.ServiceUrl = "https://marc-web-sql.marc.net/piwebapi/";
    PIWebAPIModule.PIWebAPI.CheckPIServerName(piServerName, (function (PIServerExist) { $("#PIServerExistValue").text(PIServerExist); }));
    PIWebAPIModule.PIWebAPI.CheckPIPointName(piServerName, piPointName, (function (PIPointExist) { $("#PIPointExistValue").text(PIPointExist); }));
    PIWebAPIModule.PIWebAPI.GetSnapshotValue(piServerName, piPointName, (function (data) { StartRetrievalMethod(getsnap, 'Snapshot', 'table.snapshot', ".snapshot", data) }));
    PIWebAPIModule.PIWebAPI.GetRecordedValues(piServerName, piPointName, startTime, endTime, (function (data) { StartRetrievalMethod(getrec, 'Recorded', 'table.recorded', ".recorded", data) }));
    PIWebAPIModule.PIWebAPI.GetInterpolatedValues(piServerName, piPointName, startTime, endTime, interval, (function (data) { StartRetrievalMethod(getint, 'Interpolated', 'table.interpolated', ".interpolated", data) }));
});

 

 

 

Conclusion

TypeScript can be very useful for writing more professional JavaScript files using .NET Framework concepts that we are very familiar with as PI System developers.

 

With the release of PI Web API coming soon, TypeScript can be an interesting resource tool to develop a scalable and maintainable web application having a PI System running on the backend and communicating with it through PI Web API.

 

Stay tuned for my upcoming blog posts!!

 

 

 

/// <reference path="jquery.d.ts" />
declare var $: JQueryStatic;
module PIWebAPIModule {    export class PIWebAPI {        private static baseServiceUrl: string = "NotDefined";        private static piPointLinksJsonData: any;
        constructor() { }
        static get ServiceUrl(): string {            return this.baseServiceUrl;        }        static set ServiceUrl(value: string) {            if (value.slice(-1) != "/") {                this.baseServiceUrl = value + "/";            }            else {                this.baseServiceUrl = value;            }        }
        private static baseUrlCheck(): void {            if (this.baseServiceUrl == "NotDefined") {                alert("Service base url was not defined");            }        }
        private static GetJsonContent(url: string, SuccessCallBack, ErrorCallBack): void {            $.ajax({                type: 'GET',                url: url,                cache: false,                async: true,                success: SuccessCallBack,                error: ErrorCallBack
            });        }
        public static CheckPIServerName(piServerName: string, UpdateDOM: any) {            this.baseUrlCheck();            var url: string = this.baseServiceUrl + "dataservers?name=" + piServerName;            var SuccessCallBack = function (piServerJsonData: any): void { UpdateDOM(true); };            var ErrorCallBack = function () { UpdateDOM(false); };            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);        }
        public static CheckPIPointName(piServerName: string, piPointName: string, UpdateDOM: any) {            this.baseUrlCheck();            var url: string = this.baseServiceUrl + "points?path=\\\\" + piServerName + "\\" + piPointName;            var SuccessCallBack = function (piPointJsonData: any): void { UpdateDOM(true); this.piPointLinksJsonData = piPointJsonData; };            var ErrorCallBack = function () { UpdateDOM(false); };            this.GetJsonContent(url, SuccessCallBack, ErrorCallBack);        }
        private static GetData(piServerName: string, piPointName: string, ServiceUrl: string, QueryString: string, UpdateDOM: any) {            this.baseUrlCheck();            var url: string = this.baseServiceUrl + "points?path=\\\\" + piServerName + "\\" + piPointName;            var ErrorCallBackInner = function (): void { UpdateDOM("Error: Parameters are incorrect."); };            var ErrorCallBackOutter = function (): void { UpdateDOM("Error: Could not find PI Point on the selected PI Data Archive."); };            var SecondSuccessCallBack = function (JsonData): void { UpdateDOM(JsonData); };            var FirstSuccessCallBack = (piPointJsonData) => {                var url_data: any = piPointJsonData["Links"][ServiceUrl] + QueryString;                this.GetJsonContent(url_data, SecondSuccessCallBack, ErrorCallBackInner);            }            this.GetJsonContent(url, FirstSuccessCallBack, ErrorCallBackOutter);        }
        public static GetSnapshotValue(piServerName: string, piPointName: string, UpdateDOM: any): void {            this.GetData(piServerName, piPointName, "Value", "", UpdateDOM);        }
        public static GetRecordedValues(piServerName: string, piPointName: string, startTime: string, endTime: string, UpdateDOM: any): void {            this.GetData(piServerName, piPointName, "RecordedData", "?starttime=" + startTime + "&endtime=" + endTime, UpdateDOM);        }
        public static GetInterpolatedValues(piServerName: string, piPointName: string, startTime: string, endTime: string, interval: string, UpdateDOM: any): void {            this.GetData(piServerName, piPointName, "InterpolatedData", "?starttime=" + startTime + "&endtime=" + endTime + "&interval=" + interval, UpdateDOM);        }    }}