Skip navigation
All Places > PI Developers Club > Blog
1 2 3 Previous Next

PI Developers Club

552 posts

Hello PI Geeks!

 

We are planning our next Hackathon at PI World 2018 where we expect tens of esteemed PI professionals, industry experts, and data scientists to compete. You can have your business challenge be the topic of the event which means there will be a whole group of engineers who will compete to add value to your business by solving one of your challenges.

 

We have been hosting several successful hackathons over the past few years (2017 SF, 2017 London, 2016 SF, 2016 Berlin, 2015 SF). In 2016, for example, the topic of the Programming Hackathon was Innovation Around Smart Cities. Data was sponsored by the San Diego International Airport and made available to our hackers. The executives from the airport were really happy with the final results of the hackathon mainly because:

 

  • They were inspired by the new creative apps and business models developed by our hackers, which could add a lot of value to their business.
  • They learned new ways to gain insight into the data they already had in their PI System.
  • They were able to detect where they could be more efficient in their industrial processes.

 

While starting to organize the PI World SF Hackathon 2018 we are looking to find our data sponsor. This is where you come in! We are seeking for a  customer who may be willing to share their data with us for the event. A good data sponsor typically has the following qualifications:

 

  • Owns a PI System with AF already in place
  • Has a few data-oriented high level business challenges or aspirations
  • Has at least tens of assets and many hundreds of data streams in place
  • Has at least 1 year of historical data
  • Has sampling rate of at least several samples a minute on the majority of the tags
  • Is willing to share their data with us – We are willing to consider an anonymized/obfuscated version of the dataset as well

 

In case you are interested becoming the new data sponsor for the Programming Hackathon, please don’t hesitate to contact me by e-mail (mloeff@osisoft.com).

Overview

The ability to subscribe to PI point updates through data pipes is a convenient feature which helps in both understanding and troubleshooting data flow. PIEventsNovo is a console application that can be used to sign up for data pipe (snapshot/archive/timeseries) events and additionally provides useful data access features.

 

Scenarios where this tool might be useful

-     Set up data pipe subscription for pi events (including timeseries and future data)

-     Fetching current (end-of-stream) values

-     Fetching archive values over a particular time range

-     Generating interpolated and plot values based on sample duration and pixels in trend

-     Summary calculations (Event and Time weighted) over a time range

-     Update/Annotate pi events specifying write mode (replace,insert, no compression etc) and buffer option

-     Delete recorded values over a time range

 

Note: most output formats are comma separated making it convenient to redirect the console output to csv/txt files for further investigation.

 

 

Sample outputs

 

Sign up for snapshot and archive events

 

TimeSeries data

 

Current Values

 

Archive Values

 

Summary (Event Weighted)

 

Update (with no compression and buffer event)

 

Source Code and Technical Documentation

GitHub - ThyagOSI/pieventsnovo: Application to mimic the some of the functionalities of pievents.exe. Uses AF SDK to han…

 

Minimum Requirements

.NET Framework 4.5.2

OSIsoft.AFSDK 4.0.0.0 Version 2.8.5.7759

 

Trivia

During your interactions with our technical support team you might have come across a certain 'pievents' application (inspiration for the current name and functionalities) which performs snapshot and archive data pipe subscription. However, this is not distributed or available for download and it lacks support for relatively modern features like TimeSeries and Future Data.

 

Feedback

If you find issues, or would incorporate a new feature in this utility, please feel free to post your comments.

I am pleased and excited to let you know that we will soon roll out two significant enhancements and a series of minor improvements to PI Developers Club:

  • If you are not a member yet but are interested in trying out PI DevClub member features you will be able to start a trial subscription right form the website. The goal is to let any interested person a chance to try and see how much value PI DevClub membership offers without having to pay upfront. The trial period is designed to last 45 days; at that point the subscriber will have the choice to renew and become a regular member. To be eligible two conditions need to be met: i) the individual cannot have been a member for the past two years; ii) the individual cannot have regular vouchers available to her/him to use and become a regular subscriber.
  • If you are a member already you will be able to unconsume (release) your voucher as a self-service feature; today you need to email us to do so. We believe this move gives more control to our subscribers on how to use their subscriptions.
  • Across the board we will be improving the user experience when it comes to purchase, renewal, and managing subscriptions.

 

We are planning to roll out these features over the coming weeks. Stay tuned!

As we  have already released an interesting number of PI Web API client libraries, I have created this page so you can have quick access to those bits:

 

PI Web API client library for .NET Framework

PI Web API client library for .NET Core

PI Web API client library for VBA

PI Web API client library for PHP

PI Web API client library for Java and Android

PI Web API client library for jQuery

PI Web API client library for AngularJS

PI Web API client library for Angular 4

PI Web API client library for Python (still in development)

PI Web API client library for R (still in development)

 

If you have questions, please don't hesitate to ask!

Introduction

 

Today we release our first version of the PI Web API client library for Angular 4 (the newest version of AngularJS) . The purpose of using this library is to make it easier the integration of an Angular web application with the PI System through PI Web API. This library is a client RESTful web service. All server methods from PI Web API 2017 are available on the library. As a result, you don't need to generate the URL in order to make an HTTP request. The library will generate for you automatically!

 

You can visit the GitHub repository of this library here.

 

You can visit the web page about this npm package here.

 

Although this library was tested with Angular 4, it should also be compatible with Angular 2.

 

The version for AngularJS was already released.

 

What is Angular?

 

According to the Angular official documentation:

 

"Angular is a platform that makes it easy to build applications with the web. Angular combines declarative templates, dependency injection, end to end tooling, and integrated best practices to solve development challenges. Angular empowers developers to build applications that live on the web, mobile, or the desktop."

 

Requirements

 

  • NodeJS (npm is going to be used to download the library)
  • Text Editor (Visual Studio Code is recommended)
  • Angular 4+

 

Installation

 

To install this library, run:

 

npm install angular-piwebapi --save

 

Consuming your library

 

Update your Angular AppModule by adding the PIWebAPIService as a provider:

 

import { PIWebAPIService } from 'angular-piwebapi';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
   AppComponent
  ],
  imports: [
   BrowserModule,
   FormsModule,
   HttpModule
  ],
  providers: [PIWebAPIService],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

 

Documentation

 

All classes and methods are described on the DOCUMENTATION.

 

Examples

Please check the sample_main.ts from this repository. Below there are also code snippets written in Angular 4 for you to get started using this library:

 

Set up the instance of the PI Web API top level object.

If you want to use basic authentication instead of Kerberos, set useKerberos to false and set the username and password as shown below:

 

Basic authentication

 

    this.piWebApiHttpService.configureInstance("https://devdata.osisoft.com/piwebapi/", false, "username", "password");

 

Kerberos authentication

 

    this.piWebApiHttpService.configureInstance("https://devdata.osisoft.com/piwebapi/", true);

 

If you want to test if it connects, just execute the code below:

 

    this.piWebApiHttpService.home.get().subscribe(res => {
        console.log(res);
    }, error => {
        console.log(error.json());
    });

 

Get the PI Data Archive WebId

 

    this.piWebApiHttpService.dataServer.getByPath('\\\\SERVERNAME').subscribe(res => {
        console.log(res); 
    }, error => {
        console.log(error.json());
    });

 

Create a new PI Point

 

    var pointName = "SINUSOID_TEST74" + Math.trunc(100000*Math.random());
    var newPoint = new PIPoint(null, null, pointName, null, "Test PI Point for Angular PI Web API Client", "classic", "float32", null, null, null, false);    
    this.piWebApiHttpService.dataServer.createPoint(res.WebId, newPoint).subscribe(res => {
console.log(res);
}, error => {
console.log(error.json());
});     

 

 

Get PI Point WebId

 

    this.piWebApiHttpService.point.getByPath("\\\\MARC-PI2016\\sinusoid").subscribe(res => {
        console.log(res);
    }, error => {
        console.log(error.json());
    });

 

Get recorded values in bulk using the StreamSet/GetRecordedAdHoc

 

    var point1webId = "P0QuorgJ0MskeiLb6TmEmH5gAQAAAATUFSQy1QSTIwMTZcU0lOVVNPSUQ";
    var point2webId = "P0QuorgJ0MskeiLb6TmEmH5gAgAAAATUFSQy1QSTIwMTZcU0lOVVNPSURV";
    var point3webId = "P0QuorgJ0MskeiLb6TmEmH5g9AQAAATUFSQy1QSTIwMTZcQ0RUMTU4";




    var webIds = []
    webIds.push(point1webId);
    webIds.push(point2webId);
    webIds.push(point3webId);


    this.piWebApiHttpService.streamSet.getRecordedAdHoc(webIds, null, "*", null, true, 1000, null, "*-3d", null).subscribe(res => {
        console.log(res);
    }, error => {
        console.log(error.json());
    }); 

 

Send values in bulk using the StreamSet/UpdateValuesAdHoc

 

    let streamValuesItems : PIItemsStreamValues = new PIItemsStreamValues()
    let streamValue1 : PIStreamValues = new PIStreamValues()
    let streamValue2 : PIStreamValues = new PIStreamValues()
    let streamValue3 : PIStreamValues = new PIStreamValues()


    let value1 : PITimedValue = new PITimedValue()
    let value2 : PITimedValue = new PITimedValue()
    let value3 : PITimedValue = new PITimedValue()
    let value4 : PITimedValue = new PITimedValue()
    let value5 : PITimedValue = new PITimedValue()
    let value6 : PITimedValue = new PITimedValue()


    value1.Value = 2
    value1.Timestamp = "*-1d"
    value2.Value = 3
    value2.Timestamp = "*-2d"
    value3.Value = 4
    value3.Timestamp = "*-1d"
    value4.Value = 5
    value4.Timestamp = "*-2d"
    value5.Value = 6
    value5.Timestamp = "*-1d"
    value6.Value = 7
    value6.Timestamp = "*-2d"


    streamValue1.WebId = point1webId
    streamValue2.WebId = point2webId
    streamValue3.WebId = point3webId


    var values1 = [];
    values1.push(value1)
    values1.push(value2)
    streamValue1.Items = values1


    var values2 = [];
    values2.push(value3)
    values2.push(value4)
    streamValue2.Items = values2


    var values3 = [];
    values3.push(value5)
    values3.push(value6)
    streamValue3.Items = values3


    var streamValues = []
    streamValues.push(streamValue1)
    streamValues.push(streamValue2)
    streamValues.push(streamValue3)
    this.piWebApiHttpService.streamSet.updateValuesAdHoc(streamValues, null, null).subscribe(res => {
        console.log(res);
    }, error => {
        console.log(error.json());
    });

 

Using PI Web API Batch

 

 var pirequest = {};
    pirequest["4"] = {
        "Method": "GET",
        "Resource": "https://marc-web-sql.marc.net/piwebapi/points?path=\\\\MARC-PI2016\\sinusoid",
        "Headers": {
            "Cache-Control": "no-cache"
        }
    };
    pirequest["5"] = {
        "Method": "GET",
        "Resource": "https://marc-web-sql.marc.net/piwebapi/points?path=\\\\MARC-PI2016\\cdt158",
        "Headers": {
            "Cache-Control": "no-cache"
        }
    };
    pirequest["6"] = {
        "Method": "GET",
        "Resource": "https://marc-web-sql.marc.net/piwebapi/streamsets/value?webid={0}&webid={1}",
        "Parameters": [
            "$.4.Content.WebId",
            "$.5.Content.WebId"
        ],
        "ParentIds": [
            "4",
            "5"
        ]
    };
    this.piWebApiHttpService.batch.execute(pirequest).subscribe(res => {
        console.log(res);
    }, error => {
        console.log(error.json());
    }); 

 

 

If you know the name of the action and controller, you probably need to make a call similar to:

 

PIWebAPIService.{controllerName}.{actionName}(inputs);

 

Please read the PI Web API programming reference to find out the names of the actions, controllers and inputs.

 

If you want to find out the order of the inputs, please refer to DOCUMENTATION and search for the method you want to use.

 

Developing a modern app with PI Web API client library for Angular

 

Do you remember my old blog post about Using PI Web API with Angular 2?

 

On that blog post, we have created a PIWebAPIService class in order to make HTTP requests against PI Web API.

 

This time let's rewrite the file app.component.ts in order to use the library methods instead. This is a good example about how this library should be used after downloading it with npm and updating the file app.module.ts.

 

import { Component } from '@angular/core';
import { PIWebAPIService, PIItemsStreamValues, PIPoint, PITimedValue, PIStreamValues } from 'angular-piwebapi';




export class ComboboxOption {
  value: boolean;
  name: string;
}


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  requestMode = true;
  piServerName: string;
  piPointName: string;
  startTime: string;
  endTime: '*';
  interval: '1h';
  yesOrNoOptions: ComboboxOption[] = [{ 'value': true, 'name': 'Yes' }, { 'value': false, 'name': 'No' }];
  getSnap: ComboboxOption = this.yesOrNoOptions[0];
  getRec: ComboboxOption = this.yesOrNoOptions[0];
  getInt: ComboboxOption = this.yesOrNoOptions[0];
  piServerData: any;
  piServerExistsValue: boolean;
  piServerError: any;
  piPointData: any;
  piPointExistsValue: boolean;
  webId: string;
  snapshotData: any;
  snapshotError: any;
  recordedData: any;
  interpolatedData: any;
  recordedError: any;
  interpolatedError: any;
  piPointError: any;


  constructor(private piWebApiHttpService: PIWebAPIService) { }


  defaultValues(): void {
    this.piServerName = 'PISRV1';
    this.piPointName = 'SINUSOID';
    this.startTime = '*-1d';
    this.endTime = '*';
    this.interval = '1h';
    this.getSnap = this.yesOrNoOptions[0];
    this.getRec = this.yesOrNoOptions[0];
    this.getInt = this.yesOrNoOptions[0];
  }
  
  // get data by making http calls
  getData(): void {
    this.piWebApiHttpService.configureInstance("https://devdata.osisoft.com/piwebapi", false, "webapiuser", "!try3.14webapi!");


    // switch div to display the results
    this.requestMode = false;
    // all HTTP requests are done through the  piWebApiHttpService factory object
    this.piWebApiHttpService.dataServer.getByPath('\\\\' + this.piServerName)  
      .subscribe(res => {
        this.piServerData = res;
        this.piServerExistsValue = true;
      },
      error => {
        this.piServerError = error;
        this.piServerExistsValue = false;
      });




      this.piWebApiHttpService.point.getByPath('\\\\' + this.piServerName + '\\' + this.piPointName, null, null).subscribe(res => {
        this.piPointData = res
        this.piPointExistsValue = true
        //in case of success, we will get the webId of the PI Point which will be used by other requests
        this.webId = res.WebId;
        this.piWebApiHttpService.stream.getValue(this.webId).subscribe(res => {
            //Response of the snapshot is stored on the snapshotData
            this.snapshotData = res
        }, error => {
            this.snapshotError = error


        });


        this.piWebApiHttpService.stream.getRecorded(this.webId, null, null, this.endTime, null, null, null, null, this.startTime).subscribe(res => {
            this.recordedData = res
          }, error => {
            this.recordedError = error
        });
        this.piWebApiHttpService.stream.getInterpolated(this.webId, null, this.endTime, null, null, this.interval, null, this.startTime, null).subscribe(res => {
            this.interpolatedData = res
          }, error => {
            this.interpolatedError = error.json();
        });
      }, error => {
        this.piPointError = error.data
        this.piPointExistsValue = false
    });
  }
}

 

 

Final Remarks

 

It is a great enhancement the fact that you can download this library through npm. I hope that with this new release, developing modern apps on top of the PI System will become even easier!

 

Please share your comments and suggestions below!

We celebrated the 3 winning teams already during the Awards Ceremony at the EMEA UC 2017 TechCon opening last Thursday. It's about time to do this publicly and to share some impressions with you.

 

It was again a big pleasure for me to see the participants of our Hackathon taking the challenge with great energy. I was very impressed by the solutions presented during the judging session.

 

Our Data Sponsor this time was Vitens N.V., the largest drinking water supplier in the Netherlands. Accordingly, the theme was Innovation for better drinking water.

Vitens offered flow, pressure and water quality data from their distribution network at Friesland province. Because Vitens is a non-profit company they defined the challenge around providing customers reliably with water at a constantly good quality.

 

Statistics

We saw 24 hackers forming 7 teams and enjoying our 23 hours event.

 

Hackathon Impressions

Please find the team names underneath the pictures.

 

Team The Three Triers

 

Team Living on the edge

 

Team blueDogPants

 

Team Diet Coke

 

Team Aperio

 

Team werusys Cologne

 

     Team connectPoint

 

Judging criteria

  • Creativity and Originality (5 points)
  • Potential Business Impact (5 points)
  • Technical Implementation (5 points)
  • Data Analysis and Insight (5 points)
  • User Experience (5 points)

 

Pitch Panel

This year we pulled the idea of having a Pitch Panel over the ocean.

The Pitch Panel is a business oriented session where the top teams present their work in the hackathon to a panel of business leaders from the data sponsor and OSIsoft. The goal is for the hackers to have a chance to show their work in a different environment more aligned with business visibility and opportunity. The business leaders can see the novel creations and decide if they like to pursue any of the hackers in a more advanced commercial capacity.

 

Besides the 3 winning Teams, judges decided to offer team Aperio to present in front of the Pitch Panel.

The Pitch Panel was recorded. We will be sharing a link to the video, as soon as it is ready.

 

The winning teams

 

 

 

 

 

 

Please see attached for additional impressions captured by our professional conference photographer. Thank you Sebastian!

Some customers have the occasional but pronounced need to retrieve millions upon millions of recorded values for a given PIPoint.  There is no method directly with the PI AF SDK to address this need.  Virtually every data method within the SDK has 2 known limitations:

 

  1. An operations timeout should a single data call take too long to fulfill, and
  2. No more than ArcMaxCollect values may be returned in a single data call, where ArcMaxCollect is a tuning parameter on your PI Data Archive.

 

One should not modify ArcMaxCollect on a whim.  There's a reason why the defaults are what they are (PI Server 2012+ the default is 1.5 million, earlier versions are 150K).  You would not be increasing it just for you.  The change applies to all users.  How confident are you that your own users won't be trying to fetch 200 million data values at once?  There is a workaround that you may prudently use in your code in lieu of increasing ArcMaxCollect.

 

GitHub - Rick-at-OSIsoft/pipoint-getlargerecordedvalues: A workaround that removes the limitation of ArcMaxCollect, and …

 

The GitHub repository has code versions for C# and VB.NET.

 

In a nutshell, how does this workaround get past the 2 known limitations?  By retrieving the AFValues in pages of 100K values at a time.  This is well below the default value for ArcMaxCollect, and retrieving 100K values is easily satisfied within the operations timeout.  Remember: you would want to use the GetLargeRecordedValues method when you know you have PIPoint(s) with millions and millions of recorded values.

 

Usage

Given you have a PIPoint instance in an object named tag and a defined AFTimeRange in timeRange:

 

C# Example

var values = tag.GetLargeRecordedValues(timeRange);
foreach (var value in values)
 {
    // do something with value
}

 

 

VB.NET Example

Dim values = tag.GetLargeRecordedValues(timeRange)
For Each value As AFValue in values
    ' do something with value
Next 

 

 

Cautions and What to Avoid

The biggest caution to acknowledge is that you have massive amounts of data.  The traditional best practices such as using bulk PIPointList will no longer apply.  You are in a different world if you want to loop over a tag that has 200 million data values.  There are considerations well outside the scope of AF SDK.  Two notable instances are timing and memory.  Retrieving 200 million values will be done as quickly as possible, but you have to accept that it still takes some time to consume that much data.  So the concept of "fast" goes out the window.  And if you attempt to retrieve those values into a list or array, .NET will most likely give you an out-of-memory exception long before all the values are retrieved.

 

Therefore it's best to avoid memory-hogging calls such as LINQ's ToList() or ToArray().  The best performance is to consume the values as they are being streamed without any attempt to persist to an indexed collection.  Another performance killer is using Count().  I have seen a lot of traditional applications that attempt to show a value count before looping over the values.  Since its a streamed enumerable set that isn't persisted in memory, issuing a Count() has a very negative performance consequence of retrieving and counting all the AFValues.  Subsequent passes through a loop will then require the time-consuming process of retrieving the data a second time!  If you want to display a count in your logs because it's something nice to do, you should maintain your independent count while you are looping the first time, and then display that nice count at the end rather than the beginning.

 

C# Example

var values = tag.GetLargeRecordedValues(timeRange);
int count = 0;
foreach (var value in values)
{
     ++count;
    // do something with value
}
Console.WriteLine("{0} count = {1:N0}", tag.Name, count);

Introduction

 

The PI Web API 2017 release has come with lots of great new features. One of the them is the Swagger specification which is a JSON string that allows you to generate client-side libraries in many different programming languages including Java, C#, JavaScript AngularJS etc...

 

I have already published the PI Web API client library for AngularJS. Nevertheless, there are web developers who still prefer jQuery and that are not familiar with AngularJS. Since the code is very similar (after all everything is written in Typescript which is compiled into JavaScript), I've decided to release a jQuery version.

 

On this blog post, I won't show you how to generate this library because the process is very similar to AngularJS. In case you are interested in generating your own library, please refer to the AngularJS version of this post and also visit this GitHub repository which has the generator of this library.

 

If you want to download the library please visit this GitHub repository. A sample web application using this library is available here.

 

What is Swagger?

 

If you visit the Swagger web site, this is how they define their product: "The goal of Swagger™ is to define a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined via Swagger, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interfaces have done for lower-level programming, Swagger removes the guesswork in calling the service.".

 

Please visit this page to find more information about getting started with Swagger.

 

How to access the PI Web API Swagger definition?

 

Just make a GET request against the following url: https://servername/piwebapi/help/specification?pretty=true. You will receive a JSON response back with the Swagger definition.

Remember that you must be using at least PI Web API 2017 in order to have access to the Swagger definition. This url won't work with PI Web API 2016 R2 and older versions of the product. Nevertheless, if you download the library from our GitHub repository, it will be compatible with all PI Web API versions. Just expect to receive some exceptions in case you are using a method that is not implemented on the server side as result of not using the latest PI Web API version.

 

Requirements

 

  • PI Web API 2017 installed within your domain using Kerberos or Basic Authentication.
  • jQuery 1.7.1+

 

Installation

 

  • Download this source code
  • Copy the files from the dist folder to one of your web application folders.

 

Usage

 

  • Refer to the jquery-piwebapi.js or jquery-piwebapi.min.js (make sure it is after loading the jQuery library).

 

Source Code

 

The solution that generates the final library is available on the src folder. You might want to add or edit a method and rebuild the solution in order to generate custom assemblies. The links from both GitHub repositories are below:

 

 

Documentation

 

All classes and methods are described on the DOCUMENTATION.

 

Examples

 

Please check the sample_main.js from this repository. Below there are also code snippets written in JavaScript for you to get started using this library:

 

Set up the instance of the PI Web API top level object.

 

    var piwebapi = new PIWebApi("https://marc-web-sql.marc.net/piwebapi", true); 

If you want to use basic authentication instead of Kerberos, set useKerberos to false and set the username and password accordingly.

    var piwebapi = new PIWebApi("https://marc-web-sql.marc.net/piwebapi", false, "username", "password"); 

 

Get the PI Data Archive WebId

 

    piwebapi.dataServer.getByPath('\\\\MARC-PI2016').then(function (response) { dataServer = response.data; }, function (error) { console.log(error); });

 

Create a new PI Point

 

    var newPoint = new PIWebApiClient.PIPoint(null, null, "SINUSOID_TEST124121115", null, "Test PI Point for jQuery PI Web API Client", "classic", "float32", null, null, null, false); 
    piwebapi.dataServer.createPoint(dataServer.WebId, newPoint).then(function (response) { console.log(response.data); }, function (error) { console.log(error); });       

 

Get PI Point WebId

 

    piwebapi.point.getByPath("\\\\MARC-PI2016\\sinusoid").then(function (response) { 
       var webId = response.data.WebId; },
    function (error) {
       console.log(error);
   });

 

Get recorded values in bulk using the StreamSet/GetRecordedAdHoc

 

    var webIds = [] 
    webIds.push(point1webId);
    webIds.push(point2webId);
    webIds.push(point3webId);
    piwebapi.streamSet.getRecordedAdHoc(webIds, null, "*", null, true, 1000, null, "*-3d", null).then(function (response) {
      console.log(response.data);
    }, function (error) {
      console.log(error);
    });

 

Send values in bulk using the StreamSet/UpdateValuesAdHoc

 

    streamValuesItems = new PIWebApiClient.PIItemsStreamValues() 
    streamValue1 = new PIWebApiClient.PIStreamValues()
    streamValue2 = new PIWebApiClient.PIStreamValues()
    streamValue3 = new PIWebApiClient.PIStreamValues() 
    value1 = new PIWebApiClient.PITimedValue()
    value2 = new PIWebApiClient.PITimedValue()
    value3 = new PIWebApiClient.PITimedValue()
    value4 = new PIWebApiClient.PITimedValue()
    value5 = new PIWebApiClient.PITimedValue()
    value6 = new PIWebApiClient.PITimedValue() 
    value1.Value = 2
    value1.Timestamp = "*-1d"
    value2.Value = 3 value2.Timestamp = "*-2d"
    value3.Value = 4 value3.Timestamp = "*-1d"
    value4.Value = 5 value4.Timestamp = "*-2d"
    value5.Value = 6 value5.Timestamp = "*-1d"
    value6.Value = 7 value6.Timestamp = "*-2d" 
    streamValue1.WebId = point1webId
    streamValue2.WebId = point2webId
    streamValue3.WebId = point3webId 
    values1 = []; values1.push(value1)
    values1.push(value2)
    streamValue1.Items = values1 
    values2 = []; values2.push(value3)
    values2.push(value4)
    streamValue2.Items = values2
    values3 = [];
    values3.push(value5)
    values3.push(value6)
    streamValue3.Items = values3
    streamValues = []
    streamValues.push(streamValue1)
    streamValues.push(streamValue2)
    streamValues.push(streamValue3)
    piwebapi.streamSet.updateValuesAdHoc(streamValues, null, null).then(function (response) {
          console.log(response.data);
    }, function (error) { console.log(error); });

 

Developing a web application using jQuery and PI Web API client library for jQuery

 

In order to test this new client library, let's take the example from the blog post. First we need to edit the pi_data_result.html:

 

    <script src="Scripts/jquery-3.1.1.js"></script>
    <script src="Scripts/pi_data_result.js"></script>
    <script src="Scripts/jquery-piwebapi.js"></script>

 

You might need to install jQuery NuGet package using the command on the Package Manager: Install-Package jQuery

 

The original pi_data_result.js is:

 

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


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


function StartRetrievalMethod(PerformRequest, RetrievalMethodName, TableToAdd, RetrievalMethodClass, RetrievalMethodData) {


    if (PerformRequest == "yes") {
        try {
            for (var i = 0; i < RetrievalMethodData["Items"].length; i++) {
                $('<tr/>', {
                    'id': RetrievalMethodName + 'Tr' + i,
                }).appendTo(TableToAdd);
                $('<td/>', {
                    'text': RetrievalMethodData["Items"][i].Value
                }).appendTo('#' + RetrievalMethodName + 'Tr' + i);
                $('<td/>', {
                    'text': RetrievalMethodData["Items"][i].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")
    }




}


var piServerName = "";
var piPointName = ""
var startTime = ""
var endTime = ""
var interval = ""


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


    piwebapi.SetBaseServiceUrl("https://devdata.osisoft.com/piwebapi");
    piwebapi.ValidPIServerName(piServerName).then(function () {
        $("#PIServerExistValue").text("true");
    }, function (error) {
        $("#PIServerExistValue").text("false");
    });
    piwebapi.ValidPIPointName(piServerName, piPointName).then(function (piPointData) { 
        $("#PIPointExistValue").text("true");
        piwebapi.GetSnapshotValue(piPointData).then(function (data) { 
            StartRetrievalMethod(getsnap, 'Snapshot', 'table.snapshot', ".snapshot", data) 
        }, function (error) { });
        piwebapi.GetRecordedValues(piPointData, startTime, endTime).then(function (data) { 
            StartRetrievalMethod(getrec, 'Recorded', 'table.recorded', ".recorded", data)
        }, function (error) { });
        piwebapi.GetInterpolatedValues(piPointData, startTime, endTime, interval).then(function (data) { 
            StartRetrievalMethod(getint, 'Interpolated', 'table.interpolated', ".interpolated", data);
        }, function (error) { });
    }, function () {
        $("#PIPointExistValue").text("false");
    });

});

 

 

The new pi_data_result.js looks like:

 


$(document).ready(function () {
    piServerName = GetQueryStringParams('piServerName');
    piPointName = GetQueryStringParams('piPointName');
    startTime = GetQueryStringParams('startTime');
    endTime = GetQueryStringParams('endTime');
    interval = GetQueryStringParams('interval');
    var getsnap = GetQueryStringParams('getsnap');
    var getrec = GetQueryStringParams('getrec');
    var getint = GetQueryStringParams('getint');
    $("#PIServerNameValue").text(piServerName);
    $("#PIPointNameValue").text(piPointName);
    $("#StartTimeValue").text(startTime);
    $("#EndTimeValue").text(endTime);
    $("#IntervalValue").text(interval);
    var piwebapi = new PIWebApi("https://marc-web-sql.marc.net/piwebapi", true);




    piwebapi.dataServer.getByPath('\\\\' + piServerName).then(function (response) {
        $("#PIServerExistValue").text("true");
    }, function (error) {
        $("#PIServerExistValue").text("false");
    });




    piwebapi.point.getByPath('\\\\' + piServerName + '\\' + piPointName, null, null).then(function (response) {
        $("#PIPointExistValue").text("true");


        piwebapi.stream.getValue(response.data.WebId).then(function (response) {
            StartRetrievalMethod(getsnap, 'Snapshot', 'table.snapshot', ".snapshot", response.data) 
        }, function (error) { });
        piwebapi.stream.getRecorded(response.data.WebId, null, null, endTime, null, null, null, null, startTime).then(function (response) {
            StartRetrievalMethod(getrec, 'Recorded', 'table.recorded', ".recorded", response.data) 
        }, function (error) { });
        piwebapi.stream.getInterpolated(response.data.WebId, null, endTime, null, null, interval, null, null, null, null, null, startTime).then(function (response) {
            StartRetrievalMethod(getint, 'Interpolated', 'table.interpolated', ".interpolated", response.data) 
        }, function (error) { });
    }, function () {
        $("#PIPointExistValue").text("false");
    });

});

 

 

Using the library, you don't need to write the piwebapi_wrapper.js. Just use this client library instead and it will work!

 

Conclusion

 

I really think that having PI Web API client libraries available for many different platforms with make our lives much easier. Just download it from this GitHub repository and start developing web apps using jQuery and PI Web API.

Introduction

 

Today we release our first version of the PI Web API client library for Java and Android. This library was generated using the Swagger specification available on PI Web API 2017+.

 

PI JDBC still is our official solution for retrieving PI data to Java environments. Nevertheless, there are a lot of advantages using this open-source project:

 

  • You don't need to know SQL to start coding! You just need to know how to program in Java. Since PI JDBC uses PI OLEDB Provider and PI OLEDB Enterprise under the hood, the developer needs to write SQL queries in order to retrieve/update PI data.
  • There are more methods available in PI Web API than PI JDBC. Besides, that queries against PI AF through PI JDBC and PI OLEDB Enterprise are still read-only in (written in August, 2017).
  • The machines running your Java applications don't need any additional software to be installed. This is not the case for PI JDBC, which needs to be installed on all the machines running the custom Java app.
  • You can develop Android apps with this library. PI JDBC does not allow you to do the same.

 

Requirements

 

  • PI Web API 2017 installed within your domain using Basic Authentication.
  • Building this PI Web API client library requires JDK and Maven to be installed.
  • If you are using the .NET Core version of this library, you should have .NET Core 1.1 installed on your machine.

 

Installation

 

To install the API client library to your local Maven repository, simply execute on the project folder:

 

mvn install

 

To deploy it to a remote Maven repository instead, configure the settings of the repository and execute:

 

mvn deploy

 

Refer to the official documentation for more information.

 

Usage

 

After building this client library using Maven, create a new Java project using your favorite IDE. Using Maven or Gradle, add the library according to the instructions below:

 

Maven users

 

Add this dependency to your project's POM:

 

<dependency>
  <groupId>com.osisoft.pidevclub</groupId>
  <artifactId>piwebapi</artifactId>
  <version>1.0.0</version>
</dependency>

 

Gradle users

 

Add this dependency to your project's build file:

compile 'com.osisoft.pidevclub:piwebapi:1.0.0'

 

Others

 

At first generate the JAR by executing:

mvn package 

Then manually install the following JARs:

  • target/piwebapi-1.0.0.jar
  • target/lib/*.jar

 

 

Source Code

 

The InterlliJ project that generates the final library is available on the src folder. You might want to add or edit a method and rebuild the solution in order to generate custom assemblies. The links from the GitHub repository is below:

 

 

Documentation

 

All classes and methods are described on the DOCUMENTATION.

 

Examples

 

Please check the PIWebApiTests.java from the Java test module of this repository. Below there are also code snippets written in Java for you to get started using this library:

 

Create an instance of the PI Web API top level object.

 

PIWebApiClient client = new PIWebApiClient("https://marc-web-sql.marc.net/piwebapi", username, password, false, true);  

 

 

This library is only compatible with PI Web API Basic Authentication. As a result, you must provide the username and password.

 

Get the PI Data Archive WebId

 

PIDataServer dataServer = client.getDataServer().getByPath("\\\\MARC-PI2016", null);

 

Create a new PI Point

 

PIDataServer dataServer = client.getDataServer().getByPath("\\\\MARC-PI2016, null);
PIPoint newPoint = new PIPoint();
newPoint.setName("SINUSOID_TEST5");
newPoint.setDescriptor("Test PI Point for Java PI Web API Client");
newPoint.setPointClass("classic");
newPoint.setPointType("float32");
newPoint.setFuture(false);
ApiResponse<Void> res =  client.getDataServer().createPointWithHttpInfo(dataServer.getWebId(),newPoint);     

 

Get PI Points WebIds

 

PIPoint point1 = client.getPoint().getByPath("\\\\JUPITER001\\sinusoid", null);
PIPoint point2 = client.getPoint().getByPath("\\\\JUPITER001\\sinusoidu", null);
PIPoint point3 = client.getPoint().getByPath("\\\\JUPITER001\\cdt158", null);

 

Get recorded values in bulk using the StreamSet/GetRecordedAdHoc

 

List<String> webIds = new ArrayList<String>();
webIds.add(point1.getWebId());
webIds.add(point2.getWebId());
webIds.add(point3.getWebId());
PIItemsStreamValues piItemsStreamValues = client.getStreamSet().getRecordedAdHoc(webIds,null, "*", null, true, 1000, null, "*-3d",null);

 

Send values in bulk using the StreamSet/UpdateValuesAdHoc

 

PIItemsStreamValues streamValuesItems = new PIItemsStreamValues();
PIStreamValues streamValue1 = new PIStreamValues();
PIStreamValues streamValue2 = new PIStreamValues();
PIStreamValues streamValue3 = new PIStreamValues();
PITimedValue value1 = new PITimedValue();
PITimedValue value2 = new PITimedValue();
PITimedValue value3 = new PITimedValue();
PITimedValue value4 = new PITimedValue();
PITimedValue value5 = new PITimedValue();
PITimedValue value6 = new PITimedValue();
value1.setValue(2);
value1.setTimestamp("*-1d");
value2.setValue(3);
value2.setTimestamp("*-2d");
value3.setValue(4);
value3.setTimestamp("*-1d");
value4.setValue(5);
value4.setTimestamp("*-2d");
value5.setValue(6);
value5.setTimestamp("*-1d");
value6.setValue(7);
value6.setTimestamp("*-2d");
streamValue1.setWebId(point1.getWebId());
streamValue2.setWebId(point2.getWebId());
streamValue3.setWebId(point3.getWebId());


List<PITimedValue> values1 = new ArrayList<PITimedValue>();
values1.add(value1);
values1.add(value2);
streamValue1.setItems(values1);


List<PITimedValue> values2 = new ArrayList<PITimedValue>();
values2.add(value3);
values2.add(value4);
streamValue2.setItems(values2);


List<PITimedValue> values3 = new ArrayList<PITimedValue>();
values3.add(value5);
values3.add(value6);
streamValue3.setItems(values3);


List<PIStreamValues> streamValues = new ArrayList<PIStreamValues>();
streamValues.add(streamValue1);
streamValues.add(streamValue2);
streamValues.add(streamValue3);
ApiResponse<PIItemsItemsSubstatus> res = client.getStreamSet().updateValuesAdHocWithHttpInfo(streamValues, null,null);

 

Get element and its attributes given an AF Element path

 

PIElement myElement = client.getElement().getByPath("\\\\MARC-PI2016\\CrossPlatformLab\\marc.adm", null);
PIItemsAttribute attributes = client.getElement().getAttributes(myElement.getWebId(), null, 1000, null, false, null, null,null,null,null,0,null,null);

 

Final Remarks

 

As you could realize by looking at the example, it is pretty easy to use this client library on your Java apps. Please provide your feedback by posting your comments below!

·         You don't need to know SQL to start coding! You just need to know how to program in Java. Since PI JDBC uses PI OLEDB Provider and PI OLEDB Enterprise under the hood, the developer needs to write SQL queries in order to retrieve/update PI data.

·         There are more methods available in PI Web API than PI JDBC. Besides, that queries against PI AF through PI JDBC and PI OLEDB Enterprise are still read-only in (written in August, 2017).

·         The machines running your Java applications don't need any additional software to be installed. This is not the case for PI JDBC, which needs to be installed on all the machines running the custom Java app.

·         You can develop Android apps with this library. PI JDBC does not allow you to do the same.

·         Although it was not tested, the performance should be better for calls in bulk. PI Web API client library allows you to retrieve and send data in bulk. This feature is not available on PI JDBC yet.

Introduction

 

Today we release our first version of the PI Web API client libraries for .NET Framework and .NET Core. Those libraries were generated using the Swagger specification available on PI Web API 2017+.

 

Although PI AF SDK still remains the best choice in case performance is a must, there are a lot of scenarios where it makes sense to develop your .NET applications using with those libraries:

 

  • You cannot install PI AF SDK on the machine which would run your application.
  • You are developing a web application using Azure Web Apps, which doesn't allow you to remote in and install OSIsoft products.
  • Your PI System is hosted in the cloud. Therefore, it makes to expose the data through a public endpoint of PI Web API.
  • You want to create applications that are compatible with Linux and macOS.
  • You want to use the most modern technology of the market.

 

The first two reasons of the list above applies when you are developing custom .NET Framework apps.  The last 3 items from this list applies for .NET Core. But what is .NET Core exactly?

 

What is .NET Core?

 

According to the .NET blog, .NET Core is a cross-platform, open source, and modular .NET platform for creating modern web apps, microservices, libraries and console applications. Because it is cross-platform, you can run your .NET Core apps on Windows, Linux and macOS. Users can use the command-line tools to manage their project.

 

.NET Core is composed by 3 parts: .NET runtime, a set of framework libraries and a set of SDK tools. This new technology can be need as a cross-platform version of the .NET Framework.

 

.NET Core and PI Developer Technologies

 

.NET Core is not compatible with .NET Framework libraries like PI AF SDK. OSIsoft's product management and development teams have ongoing investigations into creating a PI AF SDK version compatible with .NET Core, but at this point there is no timeline for release. Although the PI AF SDK is faster than PI Web API, the performance of our RESTful web service is sufficient for many use cases.  The PI Web API team is committed to improving performance and adding features to each new release.

 

 

If you are developing a long running service or an application that retrieves large amounts of PI data, our suggestion is to develop on top of PI AF SDK and .NET Framework. If this is not the case, using this new client library for developing .NET Core apps should suffice your needs.

 

 

Requirements

 

  • PI Web API 2017 installed within your domain using Kerberos or Basic Authentication.
  • If you are using the .NET Framework version of this library, you should have .NET Framework 4.5 installed on your machine.
  • If you are using the .NET Core version of this library, you should have .NET Core 1.1 installed on your machine.

 

Installation

 

  • Download this source code
  • Create a new folder under %PIHOME% named WebAPIClient, if it doesn't exist.
  • Create a new folder under WebAPIClient named DotNetCore, if it doesn't exist.
  • Copy the unique file from the dist folder to %PIHOME%\WebAPIClient\DotNetCore (.NET Core) or %PIHOME%\WebAPIClient\DotNetCore (.NET Framework).

 

Usage

 

.NET Framework

 

Create a new .NET Framework project (Console Application for instance). On the Solution Explorer, right click on Dependencies and then "Add Reference...". Click on the Browse button and navigate to the %PIHOME%\WebAPIClient\DotNet folder. Finally, add the OSIsoft.PIDevClub.PIWebApiClient.dll to your VS project.

 

.NET Core

 

Create a new .NET Core project (Console Application for instance). Open the Package Manager Console and run the following command to add this library to your .NET Core project.:

 

Install-Package OSIsoft.PIDevClub.PIWebApiClient -Source %PIHOME%\WebAPIClient\DotNetCore

 

Source Code

 

The Visual Studio solution that generates the final library is available on the src folder. You might want to add or edit a method and rebuild the solution in order to generate custom assemblies. The links from both GitHub repositories are below:

 

 

Documentation

 

Once the library was added to your project, you can start writing code. The beauty of this library is that you don't need to know about writing code to make HTTP requests. The library will do that under the hood. All actions and methods from all the controllers of PI Web API 2017 are available on this client library. You just have to create the PI Web API top level object and access its properties. Each property maps a PI Web API controller which has methods which maps the actions from the RESTful web service. Therefore if you want to access the GetByPath action from the Point controller just call piwebapi.Point.GetByPath().

 

All classes and methods are described on here. Even interally both libraries are different, they have the same classes and methods from the user perspective.

 

Examples

 

Please check the Program.cs from the LibraryTest project from the Visual Studio solution of this repository. Below there are also code snippets written in C# for you to get started using this library:

 

Create an instance of the PI Web API top level object.

 

     PIWebApiClient client = new PIWebApiClient("https://marc-web-sql.marc.net/piwebapi", true);  

If you want to use basic authentication instead of Kerberos, set useKerberos to false and set the username and password accordingly.

 

Get the PI Data Archive WebId

 

    PIDataServer dataServer = client.DataServer.GetByPath("\\\\MARC-PI2016");

 

Create a new PI Point

 

    PIPoint newPIPoint = new PIPoint(); newPIPoint.Name = "MyNewPIPoint" newPIPoint.Descriptor = "Point created for wrapper test" newPIPoint.PointClass = "classic" newPIPoint.PointType = "Float32" ApiResponseObject response = client.dataServer.CreatePointWithHttpInfo(dataServer.webId, newPIPoint)

 

Get PI Points WebIds

 

    PIPoint point1 = client.Point.GetByPath("\\\\marc-pi2016\\sinusoid"); PIPoint point2 = client.Point.GetByPath("\\\\marc-pi2016\\sinusoidu"); PIPoint point3 = client.Point.GetByPath("\\\\marc-pi2016\\cdt158");

 

Get recorded values in bulk using the StreamSet/GetRecordedAdHoc

 

    List<string> webIds = new List<string>() { point1.WebId, point2.WebId, point3.WebId }; PIItemsStreamValues piItemsStreamValues = client.StreamSet.GetRecordedAdHoc(webIds, startTime: "*-3d", endTime: "*");

 

Send values in bulk using the StreamSet/UpdateValuesAdHoc

 

     var streamValuesItems = new PIItemsStreamValues(); var streamValue1 = new PIStreamValues(); var streamValue2 = new PIStreamValues(); var streamValue3 = new PIStreamValues(); var value1 = new PITimedValue(); var value2 = new PITimedValue(); var value3 = new PITimedValue(); var value4 = new PITimedValue(); var value5 = new PITimedValue(); var value6 = new PITimedValue(); value1.Value = 2; value1.Timestamp = "*-1d"; value2.Value = 3; value2.Timestamp = "*-2d"; value3.Value = 4; value3.Timestamp = "*-1d"; value4.Value = 5; value4.Timestamp = "*-2d"; value5.Value = 6; value5.Timestamp = "*-1d"; value6.Value = 7; value6.Timestamp = "*-2d"; streamValue1.WebId = point1.WebId; streamValue1.Items = new List<PITimedValue>(); streamValue1.Items.Add(value1); streamValue1.Items.Add(value2); streamValue2.WebId = point2.WebId; streamValue2.Items = new List<PITimedValue>(); streamValue2.Items.Add(value3); streamValue2.Items.Add(value4); streamValue3.WebId = point2.WebId; streamValue3.Items = new List<PITimedValue>(); streamValue3.Items.Add(value5); streamValue3.Items.Add(value6); ApiResponse<PIItemsItemsSubstatus> response2 = client.StreamSet.UpdateValuesAdHocWithHttpInfo(new List<PIStreamValues>() { streamValue1, streamValue2, streamValue3 });

 

Get element and its attributes given an AF Element path

 

     PIElement myElement = client.Element.GetByPath("\\\\MARC-PI2016\\CrossPlatformLab\\marc.adm"); PIItemsAttribute attributes = client.Element.GetAttributes(myElement.WebId, null, 1000, null, false);

 

Get current value given an AF Attribute path

 

     PIAttribute attribute = client.Attribute.GetByPath(string.Format("{0}|{1}", "\\\\MARC-PI2016\\CrossPlatformLab\\marc.adm", attributes.Items[0].Name)); PITimedValue value = client.Stream.GetEnd(attribute.WebId);

 

Get Event Frames given an AF Database path

 

    PIAssetDatabase db = client.AssetData.GetByPath(path); PIItemsEventFrame efs = client.AssetData.GetEventFrames(db.WebId, referencedElementNameFilter: "myElement", referencedElementTemplateName:

 

Final Remarks

 

As you could realize by looking at the example, it is pretty easy to use this client library on your apps. Please provide your feedback by posting your comments below!

This question comes up quite a bit at PI Square.  I know of at least 5 different ways to find out my AF Client version.  In a nutshell,

 

  1. From the registry settings
  2. From Programs and Features
  3. From the file properties in File Explorer
  4. From PI System Explorer (if also loaded on the client machine)
  5. From custom code

 

Let's go over each of these.

 

Registry Settings

 

Run regedit.exe

Navigate to:    Computer\HKEY_LOCAL_MACHINE\SOFTWARE\PISystem\AF Client

Alternative:     Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\PISystem\AF Client

 

2017-07-19 09_07_09-Registry Editor.png

2017-07-19 09_06_00-Registry Editor.png

 

2017-07-19 09_07_29-Registry Editor.png

 

Programs and Features

Trying to run this may take you to Apps and features.  You could filter the list to AF and see something like:

 

2017-07-19 09_11_51-Settings.png

 

If you really want the Version  number, look to the upper right of the window for Programs and Features:

2017-07-19 09_13_40-Settings.png

 

You should then have a list where you may scroll to:

 

2017-07-19 09_12_46-Programs and Features.png

 

 

File Properties in File Explorer

 

Using File Explorer, navigate to:     %PIHOME%\AF\PublicAssemblies\4.0

 

In my example below, %PIHOME% is "C:\Program Files (x86)\PIPC" but this could be a different drive on your machine.

 

2017-07-19 09_15_33-4.0.png

 

Right-click on the file:     OSIsoft.AFSDK.dll

 

Click on the Details tab along the tab bar at the top.

 

     2017-07-19 09_16_27-OSIsoft.AFSDK.dll Properties.png

 

 

From PI System Explorer

 

Open PSE

From the menu bar at the top, click on Help

Click on About PI System Explorer ...

 

2017-07-19 09_17_46-About PI System Explorer.png

 

 

Custom AFSDK Code

 

From a C# application that has a reference to OSIsoft.AFSDK.dll, you can use:

 

Console.WriteLine((new OSIsoft.AF.PISystems()).Version);

 

Or if you already have a  using OSIsoft.AF;  statement, this shorter version will do:

 

Console.WriteLine((new PISystems()).Version);

 

Or if you despise one-liners, you may try:

 

var clientSDK = new PISystems();

Console.WriteLine( clientSDK.Version );

 

For VB.NET, you would use an  Imports OSIsoft.AF  statement, and this line of code:

 

Console.WriteLine((New PISystems()).Version)

 

And finally, if you're not a developer or don't have Visual Studio, all is not lost.  You may still try using Powershell.

 

[Reflection.Assembly]::LoadWithPartialName("OSIsoft.AFSDK")

$clientSDK = New-Object OSIsoft.AF.PISystems

Write-Host "AF Client Version:" $clientSDK.Version

 

To produce output such as:

 

GAC    Version        Location

---    -------        --------                                                                                                                        

True   v4.0.30319     C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\OSIsoft.AFSDK\v4.0_4.0.0.0__6238be57836698e6\OSIsoft.AFSDK.dll                             AF Client Version: 2.9.1.8106

 

 

There you go.  That's at least 5 different ways to get the AF Client Version number.  Are there any others that I may have missed?

A topic that I've seen posted quite a few times is "How do I get a custom symbol to change other symbols on the display?"  Without the knowledge of what's available to you, it can seem like a tricky or impossible task.  Once you know how to do it, it actually becomes quite easy. To get you started, I've written a couple of fun/useless symbols to show you the basics.  I may expand/improve the list in the future because I'm constantly learning new things about PI Vision Extensibility.

 

The Big Red Button

 

What does it do?

delete.gif

It deletes everything on your display when clicked.  It should go without saying but please don't use this on any production displays.

How does it work?

It uses the displayProvider to select all of the symbols in the display and subsequently loops though and deletes all selected symbols.

The code

HTML:

<div id="bigredbutton" ng-click="pressed()"> DELETE </div>

JS:

window.PIVisualization = window.PIVisualization || {};
(function (PV) {
     'use strict';

     function symbolVis() { }
     PV.deriveVisualizationFromBase(symbolVis);
     symbolVis.prototype.init = function (scope, elem, displayProvider, $rootScope)
          scope.pressed = function(){
               displayProvider.selectAll();
               displayProvider.getSelectedSymbols().forEach(function(sym){
                    displayProvider.removeSymbol(sym);
               });
          }
     }

     var def = {
          typeName: 'bigredbutton',
          datasourceBehavior: PV.Extensibility.Enums.DatasourceBehaviors.Single,
          visObjectType: symbolVis,
          inject: [ 'displayProvider', '$rootScope'],
          getDefaultConfig: function(){
               return{
                    DataShape: 'Value',
                    Height: 150,
                    Width: 150
               };
          }
     };
     PV.symbolCatalog.register(def);
})(window.PIVisualization);

 

The Add DataStream Button

What does it do?

AddData.gif

When the add button is hit, it searches the display for all symbols that support multiple data streams.  It then adds whatever tag or attribute is in the textbox as a datastream to each of the found symbols.

How does it work?

It uses the displayProvider to select all of the symbols in the display and subsequently loops though, checks if the DatasourceBehavior allows multiple data sources, pushes the new data source, and then deselects the symbol.

The code

HTML:

<div id='add-data-source-container'>
     <input type='text' class='text-input' ng-model='newdatasource.text' placeholder='pi:\\servername\tagname'/>
     <button class='submit-button' ng-click="add()"> Add </button>
</div>

JS:

Note: Everything except the init function is the same as the previous symbol.  For brevity, just the init function is shown.

scope.newdatasource = {
     text: ""
};

scope.add = function(){
     displayProvider.selectAll();
     displayProvider.getSelectedSymbols().forEach(function(sym){
          //check if this symbol supports multiple datasources
          if(displayProvider.getRuntimeSymbolData(sym).def.datasourceBehavior == PV.Extensibility.Enums.DatasourceBehaviors.Multiple){
               //add the datasource from the textbox
               displayProvider.getSymbolByName(sym).DataSources.push(scope.newdatasource.text);
          }
          //deselect this symbol
          displayProvider.toggleSymbolSelection(sym);
     });
}

 

The Chatting Symbols

What does it do?

SymbolComm.gif

The name of a receiver symbol (which must be of the same type) is entered into the top box along with a message in the bottom box.  Upon clicking "Send", that message is displayed in the destination's bottom box along with who sent it (in the top box).

How does it work?

This symbol is starting to show some real power.  We're modifying the receiver's scope to change its values in real time.

Before we get to the code

There is a behavior in Angular which makes reading another DOM element's scope impossible unless debugInfoEnabled is set to true.  In order for this symbol to work, you will need to edit a line in PIVisualization.app.js changing:

$compileProvider.debugInfoEnabled(enable);

to

$compileProvider.debugInfoEnabled(true);

Be aware that this may cause a performance decrease.

The code

HTML:

<div id='sayhi-source-container'>
     <input type='text' class='text-input receiver' ng-model='message.receiver' ng-click='receiverboxclicked()' placeholder='symbol name of receiver'/>
     <input type='text' class='text-input message' ng-model='message.text' placeholder='type message to receiver'/>
     <button class='submit-button' ng-click="sayhi()"> Send </button>
</div>

JS:

Note: Everything except the init function is the same as the the big red button symbol. For brevity, just the init function is shown.

var name = this.runtime.name;

scope.message = {
     receiver: "",
     text: ""
};

scope.receiverboxclicked = function(){
     scope.message.receiver = scope.message.receiver.replace(' says:','');
     scope.message.text = '';
}

scope.sayhi = function(){
     var receiver = $('#'+scope.message.receiver);
     if(receiver.length > 0){
          var receiver_scope = receiver.scope();
          if(receiver_scope.message){
               receiver_scope.message.receiver = name + " says:";
               receiver_scope.message.text = scope.message.text;
          }
          else{
               alert(name + "'s receiver, " + scope.message.receiver + ", is not of type 'sayhi' ");
          }
     }
     else{
          alert(name + "'s receiver," +scope.message.receiver + ", does not exist");
     }
}

 

The Disable Display Selection Switch (new)

GIF.gif

What does it do?

It disables selection of symbols on the display (and also disables the right click menu)

How does it work?

We're modifying every symbol's behavior by modifying something common to all of the symbols - the display provider

The code

(styling for switch omitted)

HTML:

<div style="color: white"> Selection </div>
<label class="switch">
     <input type="checkbox" ng-model="config.Enabled" ng-change="toggled()">
     <span class="slider round"></span>
</label>

 

JS:

symbolVis.prototype.init = function (scope, elem, displayProvider){
     var originalDisplayProvider = displayProvider['selectSymbol'];
     function allowClick(){
          if(scope.config.Enabled){
               displayProvider['selectSymbol'] = originalDisplayProvider;
          }
          else{
               displayProvider['selectSymbol'] = function(){};
          }
     }

     setTimeout(function() { 
          allowClick();
     }, 1000);

     scope.toggled = function(){
          allowClick();
     }
}
var def = {
     typeName: 'disableselection',
     datasourceBehavior: PV.Extensibility.Enums.DatasourceBehaviors.Single,
     visObjectType: symbolVis,
     iconUrl: 'Scripts/app/editor/symbols/ext/Icons/toggleswitch.png',
     inject: ['displayProvider'],
     getDefaultConfig: function(){
          return{
               DataShape: 'Value',
               Enabled: true,
               Height: 70,
               Width: 70,
               BackgroundColor: 'rgb(0,0,0)',
               TextColor: 'rgb(0,255,0)',
          };
     }
};

Introduction

 

Although it is considered an old technology, there are still many developers who use VBA to integrate their PI ProcessBook displays or Excel spreadsheets with the PI System. Since our most performant PI Developer Technology is PI AF SDK, which is a native .NET Framework, it cannot be used within the VBA environment. One option is to use PI SDK in VBA in order to communicate with the PI Data Archive. Nevertheless, if those developers want to work with elements, attributes or event frames, there wasn't any really good alternative.

 

Today we are releasing the first version of the PI Web API Wrapper for VBA, which is a client RESTful web service for PI Web API 2017. With this library, almost all methods available on PI Web API 2017 can be called through this library. This means you can get any element description, retrieve its attributes, search for event frames, create new PI Points, sends and get data in bulk within the VBA context.

 

 

It is always good to remember that PI Vision is the most suitable technology for any new development projects. This wrapper should be used only when PI Vision cannot be used for any reason.

 

Requirements

 

  • PI Web API 2017 installed within your domain using Kerberos or Basic Authentication.
  • PI ProcessBook 2012 SP1+
  • .NET Framework 3.5

 

Installation

 

  • Download the latest release from our GitHub repository
  • Create a new folder under %PIHOME% named WebAPIClient, if it doesn't exist.
  • Create a new folder under WebAPIClient named VBA, if it doesn't exist.
  • Copy all files from the dist folder to %PIHOME%\WebAPIClient\VBA.
  • Run as Administrator the reg.bat located on %PIHOME%\WebAPIClient\VBA in order to register the PIWebApiWrapper assmebly.

 

Usage

 

Create or edit a PI ProcessBook display. Press ALT+F11 to open Visual Basic for Applications. On the menu, click on Tools --> References. Find PIWebApiWrapper on the list box of the available reference and add it to the VBA project.

 

 

Source Code

 

The Visual Studio solution that generates the final library is available on the src folder. You might want to add or edit a method and rebuild the solution in order to generate custom assemblies.

 

Documentation

 

All classes and methods are described here on GitHub. You can also use the Object Browser from Visual Basic for Application to read the same information. Please refer to the screenshot below:

 

 

Remember

 

As this is a .NET library with COM objects and methods exposed, in order to be able to be consumed within the VBA environment, there are some things to have in mind, especially when comparing with C# development.

  • VBA is not compatible with async methods. Therefore, only sync methods are available in this library.
  • For each PI Web API action/method of each controller, there are two methods on this client library. One returns the response of the HTTP request itself and the other wraps the response on top of ApiResponse class, providing http information, such as status code. Please refer to the Get and GetWithHttpInfo methods on our documentation and you will realize the difference between them by comparing the method signature.
  • The Batch and Channel controllers are not exposed.
  • When working with data transfer objects (models) with an Items property (such as PIItemsElement), do not access or modify this property directly. Use CreateItemsArray(), GetItem(), SetItem() and GetItemsLength() instead.
  • For models that have the Value property, use SetValueWithString(), SetValueWithInt(), SetValueWithDouble() methods to set this property.
  • For the Api methods, all variables whose type are not a string must be defined. If a string variable is optional, define it as an empty string instead of Null.

 

Examples

 

There are two PI ProcessBook displays available on the Samples folder of this repository. In addition, please refer to the following examples to understand how to use this library:

 

Create an instance of the PI Web API top level object.

 

    Dim client As New PIWebApiClient
    Dim connectedToPIWebAPI As Boolean
    connectedToPIWebAPI = client.Connect("https://marc-web-sql.marc.net/piwebapi", True)

 

If you want to use basic authentication instead of Kerberos, set useKerberos to False and set the username and password accordingly. We recommend using Kerberos because it is the safest option. For basic authentication, the password needs to be hardcoded which is not recommended. If using Kerberos authentication is not an option, protect your VBA code with a password.

 

Get the PI Data Archive WebId

 

    Set dataServer = client.dataServer.GetByName(tbPIDataArchiveName.Text)

 

Create a new PI Point

 

    Dim response As ApiResponseObject
    Dim newPIPoint As New PIPoint
    newPIPoint.Name = "MyNewPIPoint"
    newPIPoint.Descriptor = "Point created for wrapper test"
    newPIPoint.PointClass = "classic"
    newPIPoint.PointType = "Float32"
    Set response = client.dataServer.CreatePointWithHttpInfo(dataServer.webId, newPIPoint)

 

Get PI Points WebIds

 

    Set point1 = client.point.GetByPath("\\" + tbPIDataArchiveName.Text + "\" + tbTagName1.Text)
    Set point2 = client.point.GetByPath("\\" + tbPIDataArchiveName.Text + "\" + tbTagName2.Text)
    Set point3 = client.point.GetByPath("\\" + tbPIDataArchiveName.Text + "\" + tbTagName3.Text)

 

Get recorded values in bulk using the StreamSet/GetRecordedAdHoc

 

    webIds = point1.webId + "," + point2.webId + "," + point3.webId
    Set compressedData = client.StreamSet.GetRecordedAdHoc(webIds, True, 1000)

 

Send values in bulk using the StreamSet/UpdateValuesAdHoc

 

 

    Call GetPIPoints
    Dim streamValuesItems As New PIItemsStreamValues
    Dim streamValue1 As New PIStreamValues
    Dim streamValue2 As New PIStreamValues
    Dim streamValue3 As New PIStreamValues
    Dim value1 As New PITimedValue
    Dim value2 As New PITimedValue
    Dim value3 As New PITimedValue
    Dim value4 As New PITimedValue
    Dim value5 As New PITimedValue
    Dim value6 As New PITimedValue


    streamValuesItems.CreateItemsArray (3)
    value1.SetValueWithInt (2)
    value1.Timestamp = "*-1d"
    value2.SetValueWithInt (3)
    value2.Timestamp = "*-2d"
    value3.SetValueWithInt (4)
    value3.Timestamp = "*-1d"
    value4.SetValueWithInt (5)
    value4.Timestamp = "*-2d"
    value5.SetValueWithInt (6)
    value5.Timestamp = "*-1d"
    value6.SetValueWithInt (7)
    value6.Timestamp = "*-2d"


    streamValue1.webId = point1.webId
    streamValue1.CreateItemsArray (2)
    Call streamValue1.SetItem(0, value1)
    Call streamValue1.SetItem(1, value2)
    Call streamValuesItems.SetItem(0, streamValue1)


    streamValue2.webId = point2.webId
    streamValue2.CreateItemsArray (2)
    Call streamValue2.SetItem(0, value3)
    Call streamValue2.SetItem(1, value4)
    Call streamValuesItems.SetItem(1, streamValue2)


    streamValue3.webId = point2.webId
    streamValue3.CreateItemsArray (2)
    Call streamValue3.SetItem(0, value5)
    Call streamValue3.SetItem(1, value6)
    Call streamValuesItems.SetItem(2, streamValue3)

    Dim response As ApiResponsePIItemsItemsSubstatus
    Set response = client.StreamSet.UpdateValuesAdHocWithHttpInfo(streamValuesItems)

 

Get AF Attribute given an AF Element path

 

    Set elem = client.element.GetByPath(ERD.CurrentContext(ThisDisplay))
    ElemDesc.Contents = elem.Description
    Dim attributes As PIItemsAttribute
    Set attributes = client.element.GetAttributes(elem.webId, 1000, False, False, False, 0)

 

Get current value given an AF Attribute path

 

  attributePath = ERD.CurrentContext(ThisDisplay) + "|" + AttrList.Text
    Set attr = client.attribute.GetByPath(attributePath)
    Set timedValue = client.Stream.GetEnd(attr.webId)
    AttrValue.Contents = timedValue.value

 

Get Event Frames given an AF database path

 

Set db = client.AssetData.GetByPath(dbPath)
Set efs = client.AssetData.GetEventFrames(db.webId, False, False, 100, True, 0, "", "*", "", elem.Name, elem.templateName, "", "", "None", "", "",

 

 

Final Remarks

 

This library will be updated for every new release of PI Web API in order to add the methods on this library which were added to the PI Web API release.

 

Please share your comments and thoughts about this new release! Will PI Web API Wrapper be useful in your upcoming projects?

Good day!!

 

We like to seek your interest level in an idea for our next programming hackathon. One of the most interesting concepts in the area of innovation and prototyping is Design Thinking.

 

In order to add more value to our hackathon and also enhance the quality of the event we are considering some mentorship and coaching opportunity on the subject during the hackathon.

 

The goal is to have a coach equip our hackers with the Design Thinking methodology. Such techniques will enable the participants to not only deliver better results at the hackathon but also take the knowledge back to their professional careers and become better PI professionals.

 

At the beginning of the hackathon we will include a presentation of 30-45 minutes to walk the participants through the process of design thinking and define the resources during the event. Throughout the event we will provide each team with a coach to consult with as well as certain level of access to the customer or someone who plays the role of a customer.

 

If we see a good amount of interest in the community we will take the next steps to include Design Thinking in our future hackathons!

Please fill in this two questions survey in order to let us know your interest: Design Thinking in OSIsoft Hackathons survey .

Thanks in advance for your support!

We are excited to present the first LATAM Regional Conference Programming Hackathonce 2017 winners!

 

The theme of this year's Hackathon was Analytics for Smarter Energy Aware Campus. UC Davis, one campus of the University of California, kindly provided 6 months of energy data. Participants were encouraged to create killer applications for UC Davis by leveraging the PI System infrastructure.

 

The participants had 10 hours to create an app using any of the following technologies:

  • PI Server 2017
  • PI Web API 2017
  • PI Vision 2017
  • PI OLEDB Enterprise 2016 R2

 

Our judges evaluated each app based on their creativity, technical content, potential business impact, data analysis and insight and UI/UX. Although it is a tough challenge to create an app in 10 hours, four groups were able to finish their app and present to the judges!

 

Prizes:

1st place: Drone Syma X5sw 2.4ghz 4 Canais Wifi Câmera , vouchers for trainings, one year free subscription to PI Developers Club

2nd place: Vouchers for trainings, one year free subscription to PI Developers Club

 

Without further do, here are the winners!

 

1st place - JLWM Engenharia

Team members: Luan Carlos Amaral Sandes; Joao Teodoro Marinho; Mateus Gabriel Santos; Willy Rodrigo de Araujo.

 

 

IMG_20170606_175253.jpg

 

They have created an app called Predictive Models and Gamification: reducing energy costs. The UC Davis' buildings would receive points if they consume less energy than estimated by the predictive models that the groups has built.

 

The team used the following technologies:

  • PI Vision

 

Here are some screenshots presented by this group!

 

 

2nd place - Connected 4.0

 

Team members:: Guilherme Tavares, Kaio Lima, Pablo Araya, Rômulo Lemes

 

They developed an app named Connected40 the Value of People.  This app is a web application that shows the buildings on a map. When the user clicks on a building, it will display the energy KPIs of the building. They've created a new KPI which is energy per person to compare the efficiency of the building among others.

 

The team used the following technologies:

  • HTML5/JavaScript
  • PI AF SDK
  • Google Maps JavaScript API

 

Here are some screenshots presented by this group!

 

 

Filter Blog

By date: By tag: