Skip navigation
All Places > PI Developers Club > Blog > 2014 > October
2014

What is ASP.NET vNext?

ASP.NET MVC was introduced in 2009 and it is distributed separately from the classic ASP.NET, increasing the delivery speed since recent ASP.NET MVC versions were delivered as a complement through NuGet, for instance.

 

If you have been developing with ASP.NET for some time in recent years, you probably have been witnessing many changes on Web API, SignalR, SPA, ASP.NET Identity since all of them were distributed separately.

 

Following the same path, ASP.NET vNext, which is actually ASP.NET MVC 6, represents a fundamental change on how Microsoft constructs and deploys web frameworks. The purpose is eliminate the dependency of the System.Web assembly since it is quite expensive in order to develop a host framework. This way, ASP.NET MVC can run not only on Windows through IIS but also on other operating system and hosts.

 

Here is what has changes:

  • Web Pages, MVC, Web API is now the same thing, called the MVC 6.
  • New MVC 6 versions optimized for cloud MVC 6, SignalR 3 and Entity Framework 7
  • Finished dependency on System.Web, MVC 6 is now a middleware and now only Web Forms depends on System.Web.
  •  Cloud-optimized versions of MVC, Web API, Web Pages, SignalR and Entity Framework.
  • Increased portability since there is no dependence on the GAC assemblies facilitating cloud deployment.
  • Ability to host your application in IIS or on a self-hosted process.
  • Support legacy Web Forms, MVC 5, Web API 2, SignalR 2 and EF 6.
  • Rosyln support. You no longer need to stop the application to change a class! Just change it, save and press F5 in the browser! That is all! Much productivity!
  • Very low memory consumption.
  • Multiplatform! Turn ASP.NET wherever you want!

Objective

On my previous blog post, I have developed a sample ASP.NET MVC 5 web application which shows snapshot values from searched PI Points through PI AF SDK. On this blog post, I will develop a similar project with ASP.NET vNext (ASP.NET MVC 6) running the web application on a self-hosted process.

 

We are using Visual Studio 14 CTP3 on Windows 8.1. Please refer to my other previous blog post to find out more information concerning download this CTP version and the procedure to install it.

Create a new project

Open Visual Studio and click on File à New Project… --> Visual C# --> Web --> ASP.NET vNext Web Application. Figure 1 shows the screenshot for creating new project.

 

 8420.fig1.png

 

Figure 1 – Creating a new  ASP.NET vNext project in Visual Studio.

 

For the name of your solution, please write “PIAFSDK-vNextWebApplication” and click on “OK”.

 

On the previous blog post, I have chosen the empty template because on the MVC template a lot of resources were available which I wouldn’t use. Therefore, the empty template is a better option for a clean solution. Nevertheless, for ASP.NET vNext, we have not chosen the ASP.NET vNext Empty Web Application because it would be more difficult to get started.

 

Please refer to my previous blog post in case you want more information about the Models, Controllers and Views in this exercise since they are very similar. Here we will only show the content of the files since they were already explained.

Add the PI AF SDK reference

If you right click on References, there won’t be an option called “Add reference…” as on ASP.NET MVC 5. The reason is that on this version references are defined on the project.json file. If you want to reference PI AF SDK, open the file project.json and add a string to the dependencies string array as shown below:

 
{
    "dependencies": {
        "OSIsoft.AFSDK":  "4.0.0.0",
        "EntityFramework.SqlServer": "7.0.0-alpha3",
        "Microsoft.AspNet.Mvc": "6.0.0-alpha3",
        "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-alpha3",
        "Microsoft.AspNet.Identity.Authentication": "3.0.0-alpha3",
        "Microsoft.AspNet.Security.Cookies": "1.0.0-alpha3",
        "Microsoft.AspNet.Server.IIS": "1.0.0-alpha3",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-alpha3",
        "Microsoft.AspNet.StaticFiles": "1.0.0-alpha3",
        "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-alpha3",
        "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0-alpha2",
        "jQuery": "1.10.2"
 
    },
    "commands": {
        /* Change the port number when you are self hosting this application */
        "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
    },
    "frameworks": {
        "net451" : { },
        "k10" : { }
    }
}

Create a new Model

On the solution explorer, right click on the Models folder and select à Add class. Name this class PIPointSnapshotModelView and click on OK. The content of this class should be: 

 
using System;
using OSIsoft.AF.Asset;

namespace PIAFSDK_vNextWebApplication.Models
{
    public class PIPointSnapshotModelView
    {
        public string PIPointName { get; set; }
        public object Value { get; set; }
        public DateTime TimeStamp { get; set; }

        public PIPointSnapshotModelView(string piPointName, AFValue snapshot)
        {
            PIPointName = piPointName;
            Value = snapshot.Value;
            TimeStamp = snapshot.Timestamp;
        }
    }
} 

Change the HomeController

As we have not chosen the “ASP.NET vNext Empty Web Application”, there is already some action methods defined for HomeController. Modify this file to implement other actions instead:

 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;
using OSIsoft.AF.PI;
using PIAFSDK_vNextWebApplication.Models;

namespace PIAFSDK_vNextWebApplication.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View("Query");
        } 

        public ActionResult Query()
        {
            return View();
        }
 

        public ActionResult Result(string piPointNameQuery, string pointSourceNameQuery)
        {
            PIServer myPIServer = new PIServers()["MARC-RRAS"];
            PIPointQuery query1 = new PIPointQuery(PICommonPointAttributes.Tag, OSIsoft.AF.Search.AFSearchOperator.Equal, piPointNameQuery);
            PIPointQuery query2 = new PIPointQuery(PICommonPointAttributes.PointSource, OSIsoft.AF.Search.AFSearchOperator.Equal, pointSourceNameQuery);
            IEnumerable<PIPoint> foundPoints = PIPoint.FindPIPoints(myPIServer, new PIPointQuery[] { query1, query2 });
            PIPointList pointlist = new PIPointList(foundPoints.Take(1000));
            List<PIPointSnapshotModelView> PIPointSnapshotValueList = new List<PIPointSnapshotModelView>();
            foreach (PIPoint pipoint in pointlist)
            {
                PIPointSnapshotValueList.Add(new PIPointSnapshotModelView(pipoint.Name.ToString(), pipoint.Snapshot()));
            }
            return View(PIPointSnapshotValueList);
        }
    }
}

Create the Views

Go to the Views\Home folder and delete the Views: Index.cshtml, About.cshtml and Contact.cshtml.

 

Create two Views: Query.cshtml and Result.cshtml on the Views\Home with the following content:

 
//Query.cshtml

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <link href="~/Content/bootstrap.css" rel="stylesheet" />
    <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
    <script src="~/scripts/jquery-1.10.2.js"></script>
    <script src="~/Scripts/bootstrap.js"></script>
    <title>Query PI Data Archive</title>
</head>
<body>
    <div class="col-sm-8 col-sm-offset-2">
        <h1>My first ASP.NET MVC with PI AF SDK</h1>
        <br /><br />
        <h4>Search for PI Points and check its snapshots.</h4>
        <br /><br />
        <div class="container">
            <div class="row">
                @using (Html.BeginForm("Result", "Home", FormMethod.Get, new { @class = "form-inline", role = "form" }))
                {
                    <div class="form-group">
                        <label class="sr-only" for="name">Search PI Point: </label>
                        @Html.TextBox("piPointNameQuery", string.Empty, new { @class = "form-control", placeholder = "PI Point" })
                    </div>
                    <div class="form-group">
                        <label class="sr-only" for="inputfile">With this Point Source: </label>
                        @Html.TextBox("pointSourceNameQuery", string.Empty, new { @class = "form-control", placeholder = "Point Source" })
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                }
            </div>
        </div>
    </div>
</body>
</html>
//Result.cshtml
 
@model List<PIAFSDK_vNextWebApplication.Models.PIPointSnapshotModelView>

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Results</title>
    <link href="~/Content/bootstrap.css" rel="stylesheet" />
    <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
    <script src="~/scripts/jquery-1.10.2.js"></script>
    <script src="~/Scripts/bootstrap.js"></script>
</head>
<body>
    <div class="col-sm-8 col-sm-offset-2">
        <h1>My first ASP.NET MVC with PI AF SDK</h1>
        <br /><br />
        <table class="table">
            <tr>
                <th>PI Point</th>
                <th>Value</th>
                <th>Timestamp</th>
            </tr>
            @foreach (var piPointSnapshotValue in Model)
            {
                <tr>
                    <td>@piPointSnapshotValue.PIPointName</td>
                    <td>@piPointSnapshotValue.Value.ToString()</td>
                    <td>@piPointSnapshotValue.TimeStamp.ToString()</td>
                </tr>
            }
        </table>
        <br />
        <center>
            <a href="@Url.Action("Index")" class="btn btn-primary">Make another search</a>
        </center>
    </div>
</body>
</html>

If you compare carefully, in this example,  I am using Url.Action instead of Html.ActionLink on Result.cshtml. The reason is that Html.ActionLink didn’t work as expected on this version of ASP.NET vNext.

Create a self-hosted web site

First of all, start your web site and make sure it behaves like the ASP.NET MVC 5 version. If it works well, right click on the project and choose “Publish…”. On the Publish Web window, choose “File System” for the publish target as shown on Figure 2. Type a custom profile name and click on “OK”.

 

7382.fig2.png

 

Figure 2 – Selecting a publish target when publishing.

 

On the next section, choose any target location. In my case, I am using c:\AFSDKApp as shown on Figure 3.

 

4861.fig3.png

 

Figure 3– Selecting a publish target when publishing.

 

Finally, click on Publish. After Visual Studio has finished copying the files to the folder, close this project and check the content of the C:\AFSDKApp folder. You will see that there is file called web.cmd. If you run this file, your web site will be running on a self-hosted process as shown on Figure 4 without being executed by Visual Studio, IIS or IIS Express.

 

 8863.fig4.png

 

 

 

Figure 4 – Running your ASP.NET vNext Web Application on a self-host process.

Conclusion

This example was tested using Visual Studio 14 CTP3. By the time of this writing, CTP 4 was already released. Although I have not tested, this example should work as well with CTP 4.

 

I hope you find this blog post interesting and stay tuned for the next one!

I remember that some vCampus community members have requested an example of using PI AF SDK with ASP.NET MVC. Hence, I have written this blog post that I expect many of you would refer to when developing your future web applications.

What is ASP.NET MVC?

ASP.NET MVC was first introduced in 2009 by Microsoft offering a new approach for developing web application.  This technology takes advantage not only of the new MVC pattern but also of the ASP.NET platform and .NET Framework. ASP.NET MVC 4 can be used in Visual Studio 2010 and 2012 while ASP.NET MVC 5 can be used in Visual Studio 2012 and 2013.

Objectives

We are going to create a web application that will search for PI Points and display the snapshots from all PI Points found using PI AF SDK. If you prefer, you can download the Visual Studio solution here.

 

Note: since the topic is not small, we are going to talk about security on a future blog post.

Create a new project

Open Visual Studio and click on File -->  New Project… -->  Visual C# -->  Web -->  ASP.NET Web Application. Figure 1 shows the screenshot for creating new project.

 

1581.fig1.png

 

Figure 1 – Creating a new  ASP.NET MVC project in Visual Studio.

 

For the name of your solution, please write “PIAFSDK-WebApplication” and click on “OK”.

 

On the next window, select the Empty template adding the folders and core references for MVC and then click on “OK”. Figure 2 shows the screenshot for selecting the template. We have chosen the empty template instead of the MVC template because the last option not only comes with the MVC binaries but also with some Controllers, Views, Layouts and the ASP.NET Identity responsible for the authentication and authorization from your web site. As we are not going to use many of those default files, the Empty template seemed to be a better option for me.

 

5556.fig2.png

 

Figure 2 – Selecting the ASP.NET template.

 

 

 

 

Add the Bootstrap package

As I am not a web designer, I am going to use the bootstrap library in order not only to improve the design quality of my web application but also to make it responsive.

 

Open Packager Manager Console and type: Install-Package Bootstrap. Visual Studio will copy not only the bootstrap files to your project but also jQuery since it is a pre-requisite for bootstrap. It will also create a new folder called Scripts where all the JS file are supposed to be stored. Some CSS files related to bootstrap are copied to the Content folder.

 

0451.fig3.png

 

Figure 3 – Solution Explorer from Visual Studio.

Create the Home Controller

Right click on the Controllers folder and select  Add --> Controller…. Under the “Add Scaffold” window, please select the option “MVC 5 Controller – Empty”. Type the name “HomeController” on the “Add Controller” window and click on “OK”.

 

The HomeController is the default controller selected in case it is not defined on the uri. It is defined on the RouteConfig.cs file under App_Start folder.

Add Actions to the Controller and create the Views

Two actions are needed for our web application to work. The action is called “Request” while the second is called “Result”. By default, the HomeController has one Action called Index. Add the Request and Result actions as shown below:

 

  public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View("Query");
        }

        public ActionResult Query()
        {
            return View();
        }

        public ActionResult Result()
        {
            return View();
        }
    }

Note that we have changed the returned View from the Index action. Previously, it would have tried to return the file Index.cshtml (the same name from the action), which does not exist.  After the change, it retrieves the file Query.cshtml.

 

Create the Views for Request and Result. In order to do so, right click on Query() and choose “Add View…”. On the “Add View” window, make sure that “Create as partial view” and "Use a layout page:" options are unchecked and click on the Add button. Repeat this same procedure for Result.

 

Both View files were created under the Home folder under the Views folder.  For both views add a paragraph within the div in order to identify them. For example, the Query.cshtml should have the content below:

 

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Query</title>
</head>
<body>
    <div>
        <p>This is the Query page.</p>
    </div>
</body>
</html>


 

Run your application. If you receive the error similar to Figure 3, you should go to References and select System.Web.Mvc. Under properties, change the Copy Local option from “False” to “True” and try again.

 

1586.fig4.png

 

Figure 4 – Initial compilation error.

 

Hopefully, it would work fine and you will be able to access the Query page (http://localhost:51670 or http://localhost:51670/Home/Query) and the Result page (http://localhost:51670/Home/Result). Remember to replace the port number to the correct one on your browser.

Add the PI AF SDK reference

Right click on reference -->  Add reference… and add the OSIsoft.AFSDK assembly version 4.0.0.0 to the project.

 

If you try to compile and run the web application, you are probably going to receive another error shown on Figure 5.

 

5355.fig5.png

 

Figure 5 – Error after adding OSIsoft.AFSDK reference.

 

This error occurs when you build as AnyCPU. Sometimes Visual Studio doesn’t know which version of PI SDK (32 or 64 bit) it should choose. If you still wants to build as AnyCPU, there are two workarounds:

 

1.  In your post-build event command line settings for your project, delete the PI SDK assemblies which the build is copying into your bin directory

 

del "$(TargetDir)osisoft.pisdk.dll"

 

del "$(TargetDir)osisoft.pisdkcommon.dll"

 

del "$(TargetDir)osisoft.pitimeserver.dll"OR

 

2.  Add the 3 assemblies above from Program Files\PIPC\PISDK\PublicAssemblies to you project, and make sure the Copy Local setting is set to false.

 

I have chosen the first option, resulting on Figure 6.

 

2783.fig6.png

 

Figure 6 – Post-build event settings on Visual Studio.

 

If you try to recompile, hopefully you won’t receive any errors.

Create a new Model

On the solution explorer, right click on the Models folder and select -->  Add class. Name this class PIPointSnapshotModelView and click on OK. The content of this class should be:

 

public class PIPointSnapshotModelView
    {
        public string PIPointName { get; set; }
        public object Value { get; set; }
        public DateTime TimeStamp { get; set; }

        public PIPointSnapshotModelView(string piPointName, AFValue snapshot)
        {
            PIPointName = piPointName;
            Value = snapshot.Value;
            TimeStamp = snapshot.Timestamp;
        }
    }

The model is sent from the controller to be displayed on the View. We have chosen these properties since we want to display the snapshot value and its timestamp from all the PI Points found.

Change the Query View

Let’s edit the Query.cshtml which is the View associated to the Query action using the content below:

 

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <link href="~/Content/bootstrap.css" rel="stylesheet" />
    <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.9.0.js"></script>
    <script src="~/Scripts/bootstrap.js"></script>
    <title>Query PI Data Archive</title>
</head>
<body>
    <div class="col-sm-8 col-sm-offset-2">
        <h1>My first ASP.NET MVC with PI AF SDK</h1>
        <br /><br />
        <h4>Search for PI Points and check its snapshots.</h4>
        <br /><br />
        <div class="container">
            <div class="row">
                @using (Html.BeginForm("Result", "Home", FormMethod.Get, new { @class = "form-inline", role = "form" }))
                {
                    <div class="form-group">
                        <label class="sr-only" for="name">Search PI Point: </label>
                        @Html.TextBox("piPointNameQuery", string.Empty, new { @class = "form-control", placeholder = "PI Point" })
                    </div>
                    <div class="form-group">
                        <label class="sr-only" for="inputfile">With this Point Source: </label>
                        @Html.TextBox("pointSourceNameQuery", string.Empty, new { @class = "form-control", placeholder = "Point Source" })
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                }
            </div>
        </div>
  </div>
</body>
</html>



Some comments about the above code snippet:

  1. Layout = null means that no Master page should be used for this page.
  2. Html.BeginForm is used to define the form which will send the query parameters to the Result action method.
  3. The query parameters are piPointNameQuery and pointSourceNameQuery. They will be sent to the Result action name.

Change the Query and Result actions

We have changed the Result Action in order to find the PI Points restricted by pointSourceNameQuery and piPointNameQuery input variables. This is achieved by using the PIPointQuery class from the OSIsoft.AF.PI namespace as well as the PIPoint.FindPIPoints method. A PIPointList object called pointlist stores the found PI Points. Finally, this object is converted to List<PIPointSnapshotModelView> which is sent to the View. Remember to change PISERVERNAME to the hostname of your PI Data Archive.

 

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View("Query");
        }


        public ActionResult Query()
        {
            return View();
        }

        public ActionResult Result(string piPointNameQuery, string pointSourceNameQuery)
        {
            PIServer myPIServer = new PIServers()["PISERVERNAME"];
            PIPointQuery query1 = new PIPointQuery(PICommonPointAttributes.Tag, OSIsoft.AF.Search.AFSearchOperator.Equal, piPointNameQuery);
            PIPointQuery query2 = new PIPointQuery(PICommonPointAttributes.PointSource, OSIsoft.AF.Search.AFSearchOperator.Equal, pointSourceNameQuery);
            IEnumerable<PIPoint> foundPoints = PIPoint.FindPIPoints(myPIServer, new PIPointQuery[] { query1, query2 });
            PIPointList pointlist = new PIPointList(foundPoints.Take(1000));

            List<PIPointSnapshotModelView> PIPointSnapshotValueList = new List<PIPointSnapshotModelView>();
            foreach (PIPoint pipoint in pointlist)
            {
                PIPointSnapshotValueList.Add(new PIPointSnapshotModelView(pipoint.Name.ToString(), pipoint.Snapshot()));
            }
            return View(PIPointSnapshotValueList);
        }
    }



Change the Result View

The View will display on a table the information stored on the List<PIPointSnapshotModelView> . We have used some bootstrap syntax such as col-sm-8 which is used to define the width of the table.

 

@model List<PIAFSDK_WebApplication.Models.PIPointSnapshotModelView>
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Results</title>
    <link href="~/Content/bootstrap.css" rel="stylesheet" />
    <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.9.0.js"></script>
    <script src="~/Scripts/bootstrap.js"></script>
</head>
<body>
    <div class="col-sm-8 col-sm-offset-2">
        <h1>My first ASP.NET MVC with PI AF SDK</h1>
        <br /><br />
        <table class="table">
            <tr>
                <th>PI Point</th>
                <th>Value</th>
                <th>Timestamp</th>
            </tr>
            @foreach (var piPointSnapshotValue in Model)
            {
                <tr>
                    <td>@piPointSnapshotValue.PIPointName</td>
                    <td>@piPointSnapshotValue.Value.ToString()</td>
                    <td>@piPointSnapshotValue.TimeStamp.ToString()</td>
                </tr>
            }
        </table>
        <br />
        <center>
            @Html.ActionLink("Make another search", "Index", "Home", new { @class = "btn btn-primary" })
        </center>
    </div>
</body>
</html>

We have added a button on the end of the page which will go back to the Query page so that the end-user can make another search with different parameters.

Testing the web application

We have finished writing our ASP.NET MVC web application. It is time to test. When you run this web application on Visual Studio, you should see the Query page below.

 

5722.fig7.png

 

Figure 7 – Screenshot of the Query page from our web application

 

Figure 10 shows a screenshot in case we want to find all PI Points which start with “sin” from any PI Point Source. After clicking on submit, it will show the table with the PI Points found on the Result page.

 

8664.fig8.png

 

Figure 8 – Screenshot of the Result page from our web application

 

Note that the design of the page has improved using bootstrap adding only a few words to the View. The query string defines the input parameters so you can edit them directly on the uri. If you click on “Make another search” it should go back to the previous page.

Conclusion

I hope you find this example interesting and you can refer to it when developing your next web applications with PI AF SDK. On the next blog post, I will test the new ASP.NET vNext with PI AF SDK using Visual Studio 14 CTP3.

Content here

PI Data Archive and PI AF SDK 2015 Beta are released!

As you probably already know, PI AF SDK 2015 Beta and PI Data Archive 2015 Beta were released during the UC EMEA 2014 on Lisbon.

 

Concerning the server-side, PI Data Archive 2015 Beta release introduces two of the major features of PI Server 2015:

  • The ability to store time series data with future time stamps and
  • The ability to migrate PI Batch.

According to the release notes of the PI AF Client 2015, this product release adds a number of new features.  Major enhancements include:

  • Support for Future Data in the PI Data Archive through the AFDataPipe, AFDataCache, and new methods for specifically differentiating current value from end-of-stream values.
  • An update to the security model to make it similar to match the model exposed by the PI Data Archives.  ACL’s on AF Objects are no longer be tied directly to Window’s principal, but are instead defined using AF defined identities.

I was interested in writing something about this new security model. That is why I have opened PI AF Explorer and tried to find out where Identities and Mapping were located. I found them under PI Asset Server Properties, but when I clicked on them, I found out that I had to wait a little to use this feature because of what is shown on Figure 1.

 

8176.fig1.png

 

Figure 1 – Screenshot of PI Asset Server Properties of PI System Explorer 2015 Beta.

 

AF Identities and Mapping cannot be used unless you update your PI AF Server to 2015 Beta, which is not released yet.

 

This is why I have decided to write about the first major features of PI AF SDK 2015 Beta Support for Future Data.

What are future PI Points?

In this new PI Data Archive release, a new pipoint attribute called “future” was introduced. If this value is set to 0, the PI Point will be an “historical tag“ behaving as in previous versions of this product. This tag will reject any data with timestamps more than 10 minutes. If this attribute is set to 1, then this PI Point will accept future data with timestamps up until January 2038. The PI SMT 2015 Beta which is installed with PI Data Archive 2015 Beta is able to create and set future PI Points. To learn more about Future Data in PI Data Archive 2015, please refer to the “Guide to PI Data Archive 2015 Beta” manual.

Preparing the environment

I have decided to use two PI Points that I have already created for the Programming Hackathon from vCampus Live! 2013: Weather_Albany_Pressure and Weather_Albany_Pressure Prediction. The first PI Point with future attribute set to 0 was used to store the historical values from the pressure in the city of Albany. The second PI Point with future attribute set to 1 was used to store the 15 days forecast of the pressure in the same city.

 

Let’s now create a test AF database (in my case “AFSDK Test”) with one element called “DataPipe” which has two attributes:

  • Historical à refers to the ”Weather_Albany_Pressure” PI Point.
  • Future à refers to the ”Weather_Albany_Pressure Prediction” PI Point.

Please refer to Figure 2 of the new AF objects created.

 

8512.fig2.png

 

Figure 2 – AF database created for this blog post.

Changes in OSIsoft.AF.PI

In order for the PI AF SDK to handle future PI Points, a few changes were made to the OSIsoft.AF.PI namespace. The main ones are:

  • The property Future was introduced in PIPoint class in order to advise if the PI Point is a future tag or not.
  • The method Snapshot( ) is deprecated, and if called, it will be routed to CurrentValue().
  • The method CurrentValue() was added to the PIPoint class.
    • For historical tags, this will result on the snapshot that we are all familiar with.
    • For future tags, it will result on a value with a current timestamp calculated by interpolation if ‘step’ attribute is off.
    • The method EndOfStream () was added to the PIPoint class.
      • For historical tags, this will return the same AFValue object of CurrentValue().
      • For future tags, it will result on a AFValue which is in the end of stream (last event of the time series).

We can find out these new features through the code snippet below:

 
        static void Main(string[] args)
        {
            PIServer myServer = new PIServers()["MARC-RRAS"];
            myServer.Connect();
            string[] myPIPointNames = new string[] { "Weather_Albany_Pressure", "Weather_Albany_Pressure Prediction" };
            IList<PIPoint> myPIPoints = PIPoint.FindPIPoints(myServer, myPIPointNames.AsEnumerable());
            foreach (PIPoint myPIPoint in myPIPoints)
            {
                if (myPIPoint.Future)
                {
                    AFValue CurrentValue = myPIPoint.CurrentValue();
                    AFValue EndOfStream = myPIPoint.EndOfStream();
                    Console.WriteLine("Current value of future PI Point {0} is {1}", myPIPoint.Name, CurrentValue.Value.ToString()); //Snapshot is deprecated
                    Console.WriteLine("End of stream value of future PI Point {0} is {1}", myPIPoint.Name, EndOfStream.Value.ToString()); //Snapshot is deprecated
                }
                else
                {
                    AFValue CurrentValue = myPIPoint.CurrentValue();
                    AFValue EndOfStream = myPIPoint.EndOfStream();
                    Console.WriteLine("End of stream value of historical PI Point {0} is {1}", myPIPoint.Name, EndOfStream.Value.ToString()); //Snapshot is deprecated
                    Console.WriteLine("Current value of historical PI Point {0} is {1}", myPIPoint.Name, CurrentValue.Value.ToString()); //Snapshot is deprecated
                }
            }
        }

Developing a solution to monitor AFDataPipes for future data

We are going to create a new solution with two projects. The first project is responsible for sending new values to the PI Data Archive and the second project is responsible for detecting data.

 

The first program will send six values to the historical PI Point from 100 to 105 and another six values from 200 to 205 to the future PI Point. There will be six iterations where on each iteration one value is going to be sent to the historical PI Point and one value to the future PI Point. For each iteration, the program will wait 10 seconds to generate new values with different timestamps. The historical values are always sent with the current timestamp while the future values has an offset of 10 minutes towards the future.

 
static void Main(string[] args)
        {
            PISystem myPISystem = new PISystems()["OSI-SERV"];
            myPISystem.Connect();
            AFAttribute myHistoricalAttribute = AFObject.FindObject(@"\\OSI-SERV\AFSDK Test\DataPipe|Historical") as AFAttribute;
            AFAttribute myFutureAttribute = AFObject.FindObject(@"\\OSI-SERV\AFSDK Test\DataPipe|Future") as AFAttribute;

            for (int i = 0; i < 7; i++)
            {
                AFValue myNewHistoricalValue = new AFValue(i + 100, new AFTime("*"));
                AFValue myNewFutureValue = new AFValue(i + 200, new AFTime("*+10m"));

                myHistoricalAttribute.Data.UpdateValue(myNewHistoricalValue, OSIsoft.AF.Data.AFUpdateOption.Replace);
                myFutureAttribute.Data.UpdateValue(myNewFutureValue, OSIsoft.AF.Data.AFUpdateOption.Replace);

                Console.WriteLine("\n\nCurrent time: {0}\n", DateTime.Now.ToString());
                Console.WriteLine("Sent historical value => Value: {0}, Timestamp: {1}", myNewHistoricalValue.Value.ToString(), myNewHistoricalValue.Timestamp.LocalTime.ToString());
                Console.WriteLine("Sent future value => Value: {0}, Timestamp: {1}.", myNewFutureValue.Value.ToString(), myNewFutureValue.Timestamp.LocalTime.ToString());

                Thread.Sleep(10000);
            }
        }

When running the first program, you will get the following output shown on Figure 3.

 

2744.fig3.png

 

Figure 3 – Output of Program 1.

 

Remember that timestamp of the values sent to the future tags has an offset of 10 minutes compared to the current time.

 

The second program will signed up for data change events for those two PI Points. Concerning historical PI Points we already know how it works. Changes to the snapshot and archives are captured by the data pipe.  For the future PI Points, it behaves according to what is set on the property myDataPipe.EventHorizonMode which specifies which events are returned by the datapipe. The main two options are:

  • AFEventHorizonMode.EndOfStream à the data pipe will only return changes to the time series for the new events added after the end of the stream after the sign up.
  • AFEventHorizonMode.TimeOffset à will return events up to Now + EventHorizonOffset. The default value of EventHorizonOffset is zero. In this case the datapipe gets new events that were on the future, are now on the present and that soon will belong to the past.

The last option will be used in our program with EventHorizonOffset  equal to five minutes. In this second program, I have chosen to use the GetObserverEvents() instead of GetUpdateEvents(). Therefore, I have created a DataPipeObserver class which implements IObserver<AFDataPipeEvent> as shown below:

 

 

 
public class DataPipeObserver : IObserver<AFDataPipeEvent>
    {
        public void OnCompleted()
        {
            Console.WriteLine("Completed");
        } 

        public void OnError(Exception error)
        {
            Console.WriteLine("Error");
        } 

        public void OnNext(AFDataPipeEvent value)
        {
            Console.WriteLine("\n{0} NEW VALUE from PI Point: {1}\n => Value: {2} and Timestamp is {3}.", DateTime.Now.ToString(), value.Value.PIPoint.Name, value.Value.Value, value.Value.Timestamp.LocalTime);
        }
    }

For every new method sent, the OnNext() method will be called.

 

On the second program, it is necessary to register an IObserver for AFDataPipeEvent with the AFDataPipe through the subscribe method. Then, all the AFDataPipeEvents received by the data pipe will be sent to the IObserver.

 

 

 
static void Main(string[] args)
        { 
            PISystem myPISystem = new PISystems()["OSI-SERV"];
            myPISystem.Connect();
            AFElement myElement = AFObject.FindObject(@"\\OSI-SERV\AFSDK Test\DataPipe") as AFElement;
            AFAttribute myHistoricalAttribute = myElement.Attributes["Historical"];
            AFAttribute myFutureAttribute = myElement.Attributes["Future"]; 

            // Sign up for updates for attributes
            using (AFDataPipe myDataPipe = new AFDataPipe())
            {
                myDataPipe.EventHorizonMode = AFEventHorizonMode.TimeOffset;
                myDataPipe.EventHorizonOffset = new TimeSpan(0, 5, 0);
                myDataPipe.AddSignups(myElement.Attributes);
                IObserver<AFDataPipeEvent> observer = new DataPipeObserver();
                myDataPipe.Subscribe(observer);
                Console.WriteLine("Starting (press any key to exit)"); 

                // create a cancellation source to terminate the update thread when the user is done
                CancellationTokenSource cancellationSource = new CancellationTokenSource();
                Task task = Task.Run(() =>
                {
                    // keep polling while the user hasn't requested cancellation
                    while (!cancellationSource.IsCancellationRequested)
                    {
                        // Get updates from pipe and process them
                        AFErrors<AFAttribute> myErrors = myDataPipe.GetObserverEvents();

                        // wait for 1 second using the handle provided by the cancellation source
                        cancellationSource.Token.WaitHandle.WaitOne(1000);
                    }

                }, cancellationSource.Token); 

                Console.ReadKey(); // this will block until the user presses a key 
                Console.WriteLine("Exiting updates");
                cancellationSource.Cancel(); // when this is called the update loop will terminate
                task.Wait(); // wait for the task to complete before taking down the pipe
            }
        }

The first and second programs were started together by setting up the solution as shown on Figure 4.

 

4718.fig4.png

 

Figure 4– Solution Property Pages

 

The output of the second program is shown on Figure 5.

 

8510.fig5.png

 

Figure 5– Output of Program 2.

 

Let’s try to understand what has happened. For the historical data as soon as the events were sent, they were captured by the data pipe.

 

For the future values, it has taken 5 minutes for the second program to display the values because the values were sent 10 minutes on the future and the EventHorizonOffset was set to 5 minutes. This scenario is useful when you have a display and you want its values to be updated when they are exactly 5 minutes on the future.

 

Although in this demo I have continuously written data to the future tag using another project from the same solution, for the TimeOffset mode, the PI AF SDK will behave the same way as if the same values were already stored on the future archive.

Conclusion

Although data pipes is a complex topic especially with future data, I hope you got a glimpse about how to use data pipes with future data. Let’s wait for any release of PI AF Server 2015 so I can write something about AF Identities and Mappings!

 

Stay tuned!

Picking up where the last post left off...

 

 

 

In our previous post, we announced the first beta release of PI Integrator for Esri ArcGIS. Now - fast forward a few months - and we're in Beta 3! It's available in the download center, and requires Windows 8/Server 2012 and an Esri ArcGIS system. Speaking candidly, as we like to do here on vCampus, Beta 3 is our Release Candidate, and we're hopeful that it will carry us to Release 1 very shortly. Barring any significant issues, the code is feature-complete and all that remains on the horizon are a few bugs to fix and edges to smooth. This means we're very interested in feedback! The beta mailbox is BetaIntegratorForEsri@osisoft.com

 

 

 

The biggest change that Beta 3 brings is a different transport between our Configuration Portal and Esri's Geo Event Processor. We previously used the WebSocket protocol to send data to Esri, but that approach ended up with some intractable reliability problems. So the new transport-of-choice to send data to Esri is our old friend, HTTP. Esri's Geo Event Processor now makes simple HTTP(S) requests to us at some configured frequency and the system is much more reliable. This actually has some other pretty great advantages (being stateless at the web tier, it scales to the cloud/farm very well; individual requests can fail without bringing down the whole chain; etc) and the overhead is trivial compared to the payload size. Our data back-end is also optimized for that, and now scales far higher than in previous beta versions.

 

 

 

We hope that if maps are your thing, and ArcGIS is your style, that you take the opportunity to try out our Integrator!

 

 

 


Cheers,
Michael van der Veeken
Brandon Perry

 



 

P.S. - On a very related topic, Esri has some great documentation about their REST API and JavaScript libraries. The Esri Integrator product exists to feed PI data into the Esri world, so there's an opportunity for some rather novel apps and widgets to interact with real-time data on maps. We're rather curious to see what the community comes up with!

 

P.P.S. - Speaking of our data back-end ("Data Relay"), it is AF SDK-based and we've learned some lessons that we intend to share with the community: mostly, in regards to making the AFDataPipe and AFDataCache objects robust. If anyone has specific questions or requests before that blog post gets written, please shoot us a message.

Filter Blog

By date: By tag: