Escolar Documentos
Profissional Documentos
Cultura Documentos
System with R
PI DEVELOPERS CLUB WHITE PAPER
OSISOFT, LLC
How to Contact Us
Worldwide Offices
Email: pidevclub@osisoft.com
Web: pisquare.osisoft.com/community/developers-club
OSIsoft Australia
Perth, Australia
Auckland, New Zealand
OSIsoft Europe
OSIsoft, Inc.
Altenstadt, Germany
Houston, TX
Johnson City, TN
Mayfield Heights, OH
Phoenix, AZ
Savannah, GA
Seattle, WA
Yardley, PA
Singapore
OSIsoft Japan KK
Tokyo, Japan
WWW.OSISOFT.COM
OSIsoft, Inc. is the owner of the following trademarks and registered trademarks: PI System, PI
ProcessBook, Sequencia, Sigmafine, gRecipe, sRecipe, and RLINK. All terms mentioned in this book
that are known to be trademarks or service marks have been appropriately capitalized. Any trademark
that appears in this book that is not owned by OSIsoft, Inc. is the property of its owner and use herein
in no way indicates an endorsement, recommendation, or warranty of such partys products or any
affiliation with such party of any kind.
RESTRICTED RIGHTS LEGEND
Use, duplication, or disclosure by the Government is subject to restrictions as set forth in
subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS
252.227-7013
Unpublished rights reserved under the copyright laws of the United States.
TABLE OF CONTENTS
Overview ..................................................................................................................................... 5
About this Document ....................................................................................................................... 5
About this White Paper .................................................................................................................... 5
What You Need to Start ................................................................................................................... 5
1. Introduction .......................................................................................................................... 6
What Is R?......................................................................................................................................... 6
What can R do best?......................................................................................................................... 6
Why integrating the PI System with R? ............................................................................................ 6
How to use this white paper?........................................................................................................... 7
2. Integrating R and .NET ........................................................................................................... 8
What is R.NET? ................................................................................................................................ 8
Installing R.NET ................................................................................................................................. 8
3. Programming with R.NET ..................................................................................................... 11
How to initialize R.NET objects? ..................................................................................................... 11
Evaluate method ............................................................................................................................ 11
R.NET Data Types ........................................................................................................................... 11
Importing objects from R ............................................................................................................... 13
Exporting objects to R .................................................................................................................... 14
4. Integrating R and Java .......................................................................................................... 16
What is JRI? .................................................................................................................................... 16
5. Programming with JRI .......................................................................................................... 19
How to initialize JRI objects? .......................................................................................................... 19
JRI Data Types ................................................................................................................................. 22
Importing objects from R ............................................................................................................... 22
Exporting objects to R .................................................................................................................... 24
Using JavaGD on your Java projects ............................................................................................... 25
3
OVERVIEW
ABOUT THIS DOCUMENT
This document is available as part of the PI Developer Club website content, under the PI
Developers Club White Paper and Tutorials category, here.
Any question or comment related to this document should be posted in the appropriate PI
Developers Club discussion forum or sent to the PI Developers Club Team at
pidevclub@osisoft.com.
It is always recommended to use the most recent version of the products above.
1. INTRODUCTION
WHAT IS R?
R is a free software programming language and software environment for statistical computing and
graphics. The R language is widely used among statisticians and data miners for developing statistical
software and data analysis. Polls and surveys of data miners are showing R's popularity has increased
substantially in recent years.
R is an implementation of the S programming language combined with lexical scoping semantics
inspired by Scheme. S was created by John Chambers while at Bell Labs. R was created by Ross Ihaka
and Robert Gentleman at the University Of Auckland, New Zealand, and is currently developed by the
R Development Core Team, of which Chambers is a member. R is named partly after the first names
of the first two R authors and partly as a play on the name of S.
R is a GNU project. The source code for the R software environment is written primarily in C, Fortran,
and R. R is freely available under the GNU General Public License, and pre-compiled binary versions
are provided for various operating systems. R uses a command line interface; however, several
graphical user interfaces are available for use with R.
R performs complex data science and it is used by over two million analysts. R has become the tool of
choice for the worlds best statisticians, data scientists and analysts.
IS
R.NET?
R.NET enables .NET Framework to collaborate with R statistical computing (R console). R.NET requires
.NET Framework 4 and native DLLs installed with R environment. You need no other extra
installations. As a result, you need to have a machine with Visual Studio 2010 or higher installed in
order to develop a project with this library. This version of R.NET does not require .NET Framework
4.5, but this product needs to be installed in case Visual Studio 2012 is your Integrated Development
Environment.
INSTALLING R.NET
How to download R.NET?
The version R.NET 1.6.5 or higher could be downloaded through NuGet Gallery.
Installing NuGet
NuGet is a free and open source package manager for the .NET Framework, which could be installed
and updated using the Visual Studio Extension Manager. To check if your copy of Visual Studio already
has the NuGet extension, look for Library Package Manager in the Tools menu of your copy of Visual
Studio.
If your copy of Visual Studio does not already have the Library Package Manager (NuGet) extension,
you can install it using the Extension and Updates.
Using the Extension and Updates
After NuGet is installed properly, you can transfer the R.NET library from the internet running the
following command in the Package Manager Console: Install-Package R.NET.Community.
The R.NET libraries will be added automatically to your Visual Studio project.
10
EVALUATE METHOD
The evaluate method is the one of the most important methods on R.NET, since the string sent, which
is the unique parameter of this method, is actually the R code that will be executed on R console. For
instance, if you want to create a new array variable (through R.NET) on memory that stores all the
values from 1 to 100, you just need one line of code:
R
x<-1:100
C#
engine.Evaluate("x<-1:100)");
R.NET
.NET Framework
character
vector
RDotNet.CharacterVector System.String[]
Note
11
integer
vector
RDotNet.IntegerVector
System.Int32[]
real vector
RDotNet.NumericVector
System.Double[]
complex
vector
RDotNet.ComplexVector
System.Numerics.Complex[]
raw vector
RDotNet.RawVector
System.Byte[]
logical
vector
RDotNet.LogicalVector
System.Boolean[]
character
matrix
RDotNet.CharacterMatrix System.String[, ]
integer
matrix
RDotNet.IntegerMatrix
System.Int32[, ]
real
matrix
RDotNet.NumericMatrix
System.Double[, ]
complex
matrix
RDotNet.ComplexMatrix
System.Numerics.Complex[,
]
raw
matrix
RDotNet.RawMatrix
System.Byte[, ]
logical
matrix
RDotNet.LogicalMatrix
System.Boolean[, ]
list
RDotNet.GenericVector
data
frame
RDotNet.GenericVector
From version
1.1. RDotNet.DataFrame class is
also available (below).
data
frame
RDotNet.DataFrame
function
RDotNet.Function
factor
RDotNet.Factor
S4
RDotNet.S4Object
System.Int32[]
System.Numerics assembly is
required for .NET Framework 4.
System.Numerics assembly is
required for .NET Framework 4.
12
C#
RCodeString = "myintvector<-1:10";
Console.WriteLine("\nR Code: " + RCodeString);
IntegerVector myIntegerVector = engine.Evaluate(RCodeString).AsInteger();
int[] myIntegerArray = myIntegerVector.ToArray();
Console.WriteLine("\nInteger Vector: ");
i = 1;
foreach (int myInteger in myIntegerVector)
{
Console.WriteLine(i + " value=" + myInteger);
i++;
}
C#
RCodeString = "myrealvector<-rnorm(5, 0, 1)";
Console.WriteLine("\nR Code: " + RCodeString);
NumericVector myNumericVector = engine.Evaluate(RCodeString).AsNumeric();
double[] myDoubleArray = myNumericVector.ToArray();
Console.WriteLine("\nNumeric Vector: ");
i = 1;
foreach (double myNumeric in myNumericVector)
{
Console.WriteLine(i + " value=" + myNumeric);
i++;
C#
RCodeString = "mycomplexvector<- 1:2 + 1i*(8:9)";
Console.WriteLine("\nR Code: " + RCodeString);
ComplexVector myComplexVector = engine.Evaluate(RCodeString).AsComplex();
Complex[] myComplexArray = myComplexVector.ToArray();
13
C#
RCodeString = "mylogicalvector<-c(FALSE, TRUE, FALSE, TRUE, FALSE)";
Console.WriteLine("\nR Code: " + RCodeString);
LogicalVector myLogicalVector = engine.Evaluate("c(FALSE, TRUE, FALSE,
TRUE, FALSE)").AsLogical();
Boolean[] myBooleanArray = myLogicalVector.ToArray();
Console.WriteLine("\nLogical Vector: \n");
i = 1;
foreach (Boolean myBoolean in myLogicalVector)
{
Console.WriteLine(i + " value=" + myBoolean.ToString());
i++;
}
EXPORTING OBJECTS TO R
Example 1 Converting string array to R.NET CharacterVector and R variable
C#
Console.WriteLine("\nR character vector\n");
string[] myStringArray = new string[] {
"PIDataLink","PIProcessBook","PIWebParts" };
CharacterVector myCharacterVector =
engine.CreateCharacterVector(myStringArray.AsEnumerable());
engine.SetSymbol("mycharvector", myCharacterVector);
engine.Evaluate("print(mycharvector)");
C#
Console.WriteLine("\nR real vector\n");
NumericVector myNumericVector = engine.CreateNumericVector(new double[] {
30.02, 29.99, 30.11, 29.97, 30.01, 29.99 });
engine.SetSymbol("mynumvector", myNumericVector);
engine.Evaluate("print(mynumvector)");
C#
Console.WriteLine("\nR raw vector\n");
byte[] myByteArray = System.Text.Encoding.ASCII.GetBytes("u03a0");
RawVector myRawVector = engine.CreateRawVector(myByteArray);
engine.SetSymbol("myrawvector", myRawVector);
engine.Evaluate("print(myrawvector)");
15
WHAT IS JRI?
JRI is a Java/R Interface which allows you to run R inside Java applications as a single thread. It
loads the R dynamic library into Java and provides a Java API to R functionality. In a sense JRI is the
inverse of rJava and both could be combined (i.e. you can run R code inside JRI that calls back to the
JVM via rJava).
Installing JDK on Windows
Detailed instructions on how to install the JDK (Java Development Kit) for your particular operating
system are available from the JDK download website. As I have only installed JDK on Windows, the
instructions below are for the Microsofts operating system.
Remember that the JDK and the documentation are separate, and you install them separately. If you
are pushed for disk space, you don't have to install the documentation because you can access it
online. The link for the current location of the online documentation for the JDK could be found here.
After downloading and installing the latest version of the Java JDK from Oracle, you need to make sure
that if you open the command prompt, the file javac.exe is found. This can be achieved by adding the
bin folder of the JDK directory (C:\Program Files\Java\jdk1.7.0_40\bin in my case) to the PATH system
environmental variable as described in this page.
Installing Eclipse
Most people know Eclipse as an integrated development environment (IDE) for Java. Today it is the
leading development environment for Java with a market share of approximately 65%.
Eclipse is created by an Open Source community and is used in several different areas because it is a
development environment for PHP, Python, Java, Android applications and other languages.
The Eclipse IDE can be extended with additional software components. Eclipse calls these software
components plug-ins. Several Open Source projects and companies have extended the Eclipse IDE or
created standalone applications (Eclipse RCP) on top of the Eclipse framework.
In order to install Eclipse, please visit the Eclipse Downloads Page.
16
Installing JRI
The recommended way to install JRI is through R. JRI comes bundled with rJava, so the best way is to
simply install rJava on the R console. It is available from CRAN, so open the R console and execute:
R
install.packages("rJava")
After running the command above rJava will be installed. A new folder called rJava is created on the
%R_HOME%\library folder. There is also a new folder called JRI under the rJava folder with all the jar
files that you need to import on your Java project.
At this point, you should have the following system environmental variable properly configured on
your system for 32-bit Windows OS. If you are using the 64-bit version of Windows, you should replace
the i386 folder to the x64 folder.
Value
R_HOME
C:\Program Files\R\R-3.3.1
C:\Program Files\Java\jdk1.7.0_40\bin;
C:\Program Files\R\R-3.3.1\library\rJava\jri\i386;
PATH
C:\Program Files\R\R-3.3.1\bin\i386;
C:\Program Files\R\R-3.3.1\library\rJava\jri;
Table 2 - Required System Environmental Variables for Java on a 32-bit Windows OS.
JavaGD
JavaGD is a package for R (see www.r-project.org) providing a Java Graphics Device, which is a device
delegating all painting functions of R to a Java class. The default implementation provides an object
of the class Canvas, which can be used in any Java application. Currently the main use of JavaGD is in
the JGR project, but it doesn't depend on it.
Installing JavaGD should just be a matter of typing install.packages("JavaGD") at your R console and
waiting - have a look at the JavaGD homepage. If errors occur, the same holds as in "Installing JRI".
You have to carefully check that all that is needed is on the LD_LIBRARY_PATH, and that you have
Java installed and all necessary paths set up correctly.
17
There is an example below. Enter these commands at your R console and you will plot a figure to a
JavaGD device. If this doesn't work, something has gone wrong with your installation. In my case, it
has only worked on R 3.3.1 x64 version (the i386 version does not work).
R
library(JavaGD)
JavaGD()
plot(c(1,5,3,8,5), type='l', col=2)
18
package TestingJRI;
import org.rosuda.JRI.Rengine;
19
In case you are running a 64 bit Java Application, before you run your application, right click on the
project and select Run As Run Configurations.
Under
the
arguments
tab,
type
-Djava.library.path="C:\Program
3.3.1\library\rJava\jri\x64" on the VM arguments.
Files\R\R-
20
On the environment tab, create a variable called PATH with a value of C:\Program Files\R\R3.3.1\bin\x64.
Then, click on the Run button. You are supposed to see the following output on the Eclipse console:
Creating Rengine (with arguments)
Rengine created, waiting for R
Finished loading R
21
If your program is not working properly check if the system enviromental variables are well configured.
JRI
Java
Character vector
REXP
String[]
Integer vector
REXP
int[]
Real vector
REXP
double[]
Boolean vector
REXP
boolean[]
22
i++;
}
//R Boolean
RCodeString = "mybool<-c(FALSE)";
System.out.println("\nR Code: " + RCodeString);
RBool myBool = re.eval(RCodeString ).asBool();
System.out.println(i + " value=" + myBool.toString());
}
}
23
EXPORTING OBJECTS TO R
In order to transfer data from Java variables and stored them on R, please refer to the following
example.
import org.rosuda.JRI.Rengine;
import org.rosuda.JRI.REXP;
System.out.println("\n\nExporting Objects\n\n");
REXP x = null;
x=new REXP();
System.out.println(x.toString());
//R character vector -- R.NET RDotNet.RDotNet.CharacterVector
System.out.println("\nR character vector\n");
String[] myStringArray = new String[] {
"PIDataLink","PIProcessBook","PIWebParts" };
re.assign("mystringarray", myStringArray);
System.out.println(x=re.eval("mystringarray"));
24
}
}
engine.eval("library(JavaGD)");
engine.eval("JavaGD()");
It is possible to draw your own implementation of JavaGD device (and not the default one). In this
scenario, the function JavaGD() calls the implementation of JavaGD found in the class defined by the
environment variable JAVAGD_CLASS_NAME. Hence, it is necessary to create a class that extends
GDInterface, set JAVAGD_CLASS_NAME to the name of your class. The easiest way of doing this is
having the Rengine evaluate "Sys.putenv('JAVAGD_CLASS_NAME'= 'MyJavaGD1')" as shown on the
code snippet below.
engine.eval("library(JavaGD)");
engine.eval("Sys.putenv('JAVAGD_CLASS_NAME'='MyJavaGD1')");
engine.eval("JavaGD()");
Finally, it needed to compile making sure that this time, you also have javaGD.jar on your classpath.
This file should be located on the library folder of R, since it was installed through the R console.
25
INSTALLING R PACKAGES
In order to use all the R functions described on this white paper, there are some packages that
should be downloaded from the internet. To download it, just open R console and type:
R
install.packages("packagename",lib=.Library)
R FUNCTIONS
Seven R functions were chosen to be used on this white paper. Data from PI Points shown on this
document are real values related to Power and Temperature.
PI Histogram
This function computes a histogram of the given data values. On the example below, it is shown the
histogram of a tag which is the outside temperature of OSIsoft San Leandro Office. This function lets
you choose the number breakpoints as well as the color of the graphic.
26
R
#Simple histogram - v is a vector of numercial values - breaks is the number of breaks
in the histogram
PI_Histogram <- function(tag1,n_breaks=5,xname="tag1",n_density=0,n_col=1){
hist(tag1, breaks=n_breaks,main = "Histogram of Outside
Temperature",density=n_density,col=n_col,xlab="Values")
}
PI Density Plot
This function calculates and plots the density of a select tag. The generic function density computes
kernel density estimates. Its default method does so with the given kernel and bandwidth for
univariate observations.
It lets you choose the color of the graphic as well as the ASP which is the ratio between axis y and x.
27
R
#Plotting density fundtion - It calculates and plots the density of numerical vector v
PI_Density_Plot <function(v,s_col="red",s_border="blue",s_type="l",n_asp=NULL,tagname="tag"){
d <- density(v[!is.na(v)])
plot(d, main="Distribution of Outside Temperature",type=s_type,asp=n_asp)
polygon(d, col=s_col, border=s_border)
}
PI Density Compare
This function is used if you want to compare the density between 2 or 3 tags. Outside and inside
temperature are compared. The package sm is required.
28
R
#Comparing density functions of two vectors ; can be extended to more than two vectors
PI_Density_Compare_TwoTags <- function(v,w,tag1name,tag2name){
require(sm)
sm.density.compare(c(v,w), c(rep(1, length(v)),rep(2, length(w))),xlab =
paste("Values"))
title(main="Comparison of Distributions")
legend("topright", c(1, 2), fill=2+0:1, legend=c("Outside Temperature", "Inside
Temperature"))
}
29
PI Box Plot
R
#Box plot of two tags compared - v and w are two numerical vectors
PI_Box_Plot <function(v,w,tag1name,tag2name,d_range=1.5,d_boxwex=0.8,d_staplewex=0.5){
boxplot(v,w,names=c("Outside Temperature","Inside
Temperature"),range=d_range,pars=list(boxwex=d_boxwex,staplewex=d_staplewex),main =
"Boxplot of Temperatures")
}
PI Regular Correlation
Correlations are useful because they can indicate a predictive relationship that can be exploited in
practice. For example, an electrical utility may produce less power on a warm and sunshine day based
on the correlation between electricity demand and weather.
30
R
#Regular Correlation between tags - v and w are two numerical vectors
PI_Regular_Correlation<- function(v,w,tag1name="tag1",tag2name="tag2"){
plot(v, w, xlab="Output Temparature", ylab="Total Power Demand", main="Scatter Plot")
}
One interesting feature to add on this function on your Windows Application is to show a Message
Box with the correlation coefficient before plotting the graphic. This coefficient varies between -1 and
1 and it is used as a measure of how correlated both vectors are.
PI Smooth Scatter
When some parts of the graphic generated using the PI Regular Correlation function is concentrated
with lots of dots, it is not possible to realize the level of concentration compared to other areas of the
graphic. PI Smooth Scatter function is a better option in this case. The levels of concentration of dots
are converted on tones of colors. The darker the color indicates a higher concentration of dots. This
function produces a smoothed color density representation of the scatterplot, obtained through a
kernel density estimate.
31
R
#Smooth Scatter between tags
PI_Smooth_Scatter<- function(v,w,tag1name="tag1",tag2name="tag2"){
smoothScatter(v, w, xlab="Output Temparature", ylab="Total Power Demand",main="Smooth
Scatter Plot")
}
PI Multi-Correlation
This function plots a graphic with multiple possible correlations between two tags in a group of three.
It requires the package gclus. The color on the background is related to the level of correlation
between the two tags, which could be measured using the correlation coefficient. The darker the
color, the more correlated are variables. With this function you can generate a big graphic showing all
possible correlations between five PI Points as shown on Figure 14.
32
R
#Multi-Correlation between 3 tags
PI_Multi_Correlation3<- function(impact,tagnames){
tag<-tagnames
require(gclus)
library(gclus)
impact.r <- abs(cor(impact))
impact.col <- dmat.color(impact.r)
impact.o <- order.single(impact.r)
i=1
while (i<=length(impact.o))
{
tag[i]=tagnames[impact.o[i]]
i=i+1
}
cpairs(impact, impact.o, panel.colors=impact.col, gap=0.5, main="Color-coded
Correlation Matrix",labels=c(tag[1],tag[2],tag[3]))
}
33
R Source Function
This script calls the functions in the library. To call it I use the following command in R. Make sure the
paths are correct. Also notice that the \\ should be used instead of \ in the path because \ is the
escape character in the strings in R.
R
#This script incorporates the functions defined in Functions.R and is the one supposed
to be called and run directly
source("C:\\Program Files\\R\\R-2.15.1\\library\\Functions.R")
34
PI AF SDK
R.NET
JRI
PI Web API
PI JDBC
AF & PI
Servers
PI Web API
PI AF SDK
The PI AF SDK is a proprietary, low-level, object oriented library to program with AF. This library
exposes many classes that give you read and write access to various AF databases: Elements,
Attributes, AF Analysis Rules (ARs), and Data References (DRs). It also allows you to build AF
connectivity models and cases. Configuration of notification generators are done within the AF SDK.
The AF SDK library is written as native set of .NET assemblies. The .NET AF SDK library is installed in
the Global Assembly Cache (GAC) and is made of different assemblies:
OSIsoft.AF.Security.UI.dll;
OSIsoft.AF.UI.dll;
OSIsoft.AFSDK.dll.
Only the third library needs to be referenced on your project in case graphical user interface of the PI
AF SDK is not needed.
35
C#
For more information please refer to the AF SDK Library Reference (file afsdk.chm), located on
%pihome%\HELP. This file is copied on this folder only if PI AF Developer Tools is installed on your
machine. The setup kit could be downloaded on the OSIsoft TechSupport Download Center.
PI JDBC
PI JDBC works in the same manner as the PI OLEDB Provider but offers an interaction with data
consumers (applications) that run on operating system that can be different than Windows.
PI JDBC uses Java API functions to send SQL statements to the PI Data Access Server (PI SQL DAS). The
PI SQL DAS implements a SQL engine allowing relational queries to be run against the PI System by
translating each query into a series of PI SDK calls. The PI SQL Data Access Server executes only under
36
the Windows operating system, meaning that an application built to be run under a Linux operating
system (it supports JDBC) would have to:
Send the query to the PI SQL Data Access Server using the Java PI API provided;
The PI SQL Data Access Server SQL engine would parse the query and translate it into PI SDK calls that
would then be transmitted to the PI Server (data store).
Two (2) layers must be cleared before the query arrives to the PI Server as opposed to only one for
the PI OLEDB provider. It also requires the PI SDK to allow the PI Data Access Server to function
properly. PI JDBC can only be used in Java and not in .NET environment.
PI WEB API
PI Web API provides basic functionality needed to retrieve and manipulate Time Series, Asset, and
Event Frame data. It is actually a RESTful web service on top of the PI System. There are a lot of content
about this product on PI DevClub:
37
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
using (StreamReader sw = new StreamReader(response.GetResponseStream()))
{
using (JsonTextReader reader = new JsonTextReader(sw))
{
return JObject.ReadFrom(reader);
}
}
}
The code above can only be used on .NET Framework 4.5 as the asynchronous calls were added only
in that version.
For more information please refer to the PI Web API help accessing the page
https://webserver/piwebapi/help.
As PI Web API is a RESTful web service, it is compatible with the majority of frameworks including
Android SDK, iOS and probably PHP and Python. PI Web API is your best friend in case you are
developing an Android/iOS app which needs to retrieve data from the PI System.
38
System/Language
Applications
R library
PI System
Access
technology
Repository
Perl
WWW,
Interfaces
RSPerl
PI Web API
Omegahat
Python
WWW,
Interfaces
RSPython,
rpy
PI Web API
Sourceforge
Omegahat,
Java
Interfaces,
Graphics,WWW
rJava, JRI
PI JDBC
CRAN, RForge
Oracle, MySQL
Database
ROracle,
RmySQL
PI OLEDB
Enterprise
CRAN
.NET Framework
Interfaces,
Graphics, WWW
R.NET
PI AF SDK
39
9. DEVELOPING APPLICATIONS
APPLICATION WITH GUI TO GENERATE GRAPHICS
There are three sample applications that generates R graphics with PI System data. All of them are
available on this folder from the white paper GitHub repository.
Windows Forms Application
One interesting Visual Studio project that you could be developed using R.NET is a Windows Form to
plot interesting graphics. Figure 16 shows us the dataflow of this type of project which is
unidirectional:
Firstly, data is sent to the application using PI AF SDK 2.6 (2014) which has Rich Data Access. This could
be done using the namespace OSIsoft.AF.PI, since it gives you the ability to connect directly to a PI
Server. Please refer to our webinar about PI AF SDK with RDA.
Once data is stored on PI AF SDK objects, it should be converted to R.NET objects and then be sent to
the R console, which is the feature responsible to plot the graphics.
There is a sample Windows Form application that you can download to use as a reference for
developing similar application. Here is what you should do:
1. Connect to the PI Server then choose the R function used to plot your graphic.
2. Type the PI Point names, start time and end time. The end user should choose between getting
data using Recorded Values or Interpolated Values.
3. If the option Interpolated Values is chosen, the end user should also define the interval.
4. With all of this information, a graphical will appear after the button to generate graphics is
pressed.
Figure 17 shows the Windows Form sample application.
40
If you want to understand how this application works, please read the previous chapters about R
functions to generate graphics and about PI AF SDK, PI Web Services and PI Web API in order to
retrieve data from the PI System. You can also download the sample application to view its source
code.
Java Application
A very similar project could be developed on Java but using JRI + JavaGD instead of R.NET. PI JDBC, PI
Web Services or PI Web API should be used instead of PI AF SDK, since this library is not compatible to
Java. Figure 18 shows the Graphic User Interface of a similar application running on Java.
41
Web Application
Web application and mobile apps are becoming more and more popular. This is why it is important to
be able to generate R graphics and display it on a web site or mobile app on the fly. If we try to use a
similar code from the Windows Form Application on a web site, it would pop up an R window on the
web server, instead of rendering an image on the client.
There is a web page that explains how to use ASP.NET with R.NET with a great sample on one of its
repositories. By referring to this example, a web application was developed and added to our GitHub
repository, retrieving data from the PI System and rendering SVG files to the web clients.
42
43
There
are
two
files
which
are
located
on
%pihome%\ACE\Scheduler
%pihome%\ACE\Scheduler\x86. Both of them should have the content above.
and
Below there are step-to-step instructions about how to create a PI ACE Project with R.NET that has
two input tags: sinusoid and cdt158 and three output tags Sd_sinusoid, Sd_Cdt158 and
Cor_cdt158_sinusoid.
On Visual Studio, create a new PI ACE projects selecting the input and output tags like a regular project.
Right click on the name of your project on your Solution Explorer window and select Properties.
Select the Compile ribbon and then click on the button Advanced Compile Options. A window
called Advanced Compiler Settings will appear. For the target framework, choose .NET Framework
4. On Target CPU, please select an option compatible with the value of the key PIACEHostTargetCPU
defined on the file PIACENetScheduler.exe.config.
The dataflow from PI to R is very similar to the Windows Application but in this case PI SDK and PI ACE
Libraries are used instead of PI AF SDK. If your final goal is to store the results in PI tags instead of
plotting some graphics, this option will address this objective.
44
On R, the standard deviation and correlation functions would be used and the results will be stored
on R variables.
The results are transferred back to PI ACE through R.NET.
Then, the R.NET objects are converted to PI SDK/PI ACE objects.
Finally, the values could be sent to the output tags using the method PutVal of the PI ACE library.
VB.NET
Dim sinusoid_val As NumericVector =
engine.CreateNumericVector(sinusoid_ArrayValues)
Dim cdt160_val As NumericVector =
engine.CreateNumericVector(cdt160_ArrayValues)
engine.SetSymbol("cdt160_val", cdt160_val)
engine.SetSymbol("sinusoid_val", sinusoid_val)
Dim Cor As NumericVector = engine.Evaluate("cor<-cor(sinusoid_val,
cdt160_val)").AsNumeric()
Dim Sd_cdt160 As NumericVector = engine.Evaluate("sd_cdt160<sd(cdt160_val)").AsNumeric()
Dim Sd_sinusoid As NumericVector = engine.Evaluate("sd_sinusoid<sd(sinusoid_val)").AsNumeric()
Above, there is an example about how to convert a double array to R.NET object using the SetSymbol
method. It is also shown how to use R function and send its results to your .NET application.
For console applications you need to set some parameters in order to specify the hostname,
pointsource, location1, etc
When the application is started, it will connect to the PI Server which was specified on the hostname
parameter. Then, it will search for all tags with same point source and location1. For each one of the
found PI Points, the application will read the content of the file whose path was described on the
ExDesc point attribute, and create a vector on the memory of the application.
The frequency that the application sends data to PI is also defined on the parameter. Every cycle, the
application sends a value of the vector generated with the file (that contains the R code) defined of
the ExDesc of each tag. The next cycle will send the next value of its related vector.
45
The random values could be generated using the R code on the text files that would be ready by this
application. For instance:
R
x<-rnorm(1:100)
46
RClr
RCulr
RODBC
R
install.packages("rClr",lib=.Library)
R
library("rClr")
R
source('C:\\Program Files\\R\\R-3.3.1\\library\\Functions.R');
Before writing any code, loading PI AF SDK library into R is needed using the clrLoadAssembly
function from rClr:
47
R
clrLoadAssembly("OSIsoft.AFSDK, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=6238be57836698e6")
Import to R only the PI AF SDK and call its function directly using rClr.
Import to R not only the PI AF SDK but also a custom .NET library, which will be called by rClr.
This option is more interesting since writing .NET code should be easier than writing R code
using some rClr functions to call PI AF SDK. This way, rClr will instantiate an object of the
custom .NET library and call a method which will call all PI AF SDK methods under the hood.
As the second approach was chosen, a new Visual Studio Class Library project was created with
PISystemWrapper class as the main class. The content of this class is:
C#
public class PISystemWrapper
{
private double[] values1 = null;
private double[] values2 = null;
private double[] values3 = null;
public void GetPIData(string piDataArchiveName, string piPointName1, string
piPointName2, string piPointName3, string startTime, string endTime, string interval)
{
IEnumerable<string> piPointNames = new List<string> { piPointName1,
piPointName2, piPointName3 };
PIServer piServer = new PIServers()[piDataArchiveName];
if (piServer == null)
{
return;
}
IList<PIPoint> points = PIPoint.FindPIPoints(piServer, piPointNames);
PIPointList pointList = new PIPointList(points);
AFTimeRange timeRange = new AFTimeRange(new AFTime(startTime), new
AFTime(endTime));
AFTimeSpan timeSpan;
bool result = AFTimeSpan.TryParse(interval, out timeSpan);
if (result == false)
{
AFTimeSpan.TryParse("1h", out timeSpan);
}
IEnumerable<AFValues> valuesList = pointList.InterpolatedValues(timeRange,
timeSpan, string.Empty, false, new PIPagingConfiguration(PIPageType.TagCount, 100));
48
Therefore, on R what you need to do in order to get data from the PI System with the
PISystemWrapper class is:
49
As a result, the R code below will retrieve data from the PI System through PISystemWrapper with
only some lines of code:
R
clrLoadAssembly('c:/1/rClrDotNetLibrary.dll')
w <- clrNew("rClrDotNetLibrary.PISystemWrapper")
clrCall(w, "GetPIData","marc-pi2016", "FAC.OAK.Weather-Inside_Humidity-Val.PV",
"FAC.OAK.Weather-Inside_Temperature-Val.PV", "FAC.OAK.Weather-Outside_TemperatureVal.PV", "1-Oct-2012", "1-Nov-2012", "1h");
v1=clrCall(w, "GetValues",as.integer(1))
v2=clrCall(w, "GetValues",as.integer(2))
v3=clrCall(w, "GetValues",as.integer(3))
If you take a look at the afsdk.R file on the GitHub repository, you will see a complete example of
retrieving PI data with rClr and PI AF SDK, and generating a graphic showing the correlations among
the streams.
On the previous section, it was shown how to get interpolated values using rClur and PI AF SDK. Lets
retrieve those same values using rCurl and PI Web API.
50
library("RCurl")
library("rjson")
getHttpRequest
<- function(compl_url) {
base_url = 'https://marc-web-sql.marc.net/piwebapi'
url = paste(c(base_url, compl_url),collapse="");
w=getURL(url,httpheader = c(Authorization = "Basic bsdgyYy5hZG06a2s="),
ssl.verifypeer = FALSE);
x=fromJSON(w);
}
tag1name = 'FAC.OAK.Weather-Inside_Humidity-Val.PV';
tag2name = 'FAC.OAK.Weather-Inside_Temperature-Val.PV';
tag3name = 'FAC.OAK.Weather-Outside_Temperature-Val.PV';
webId1 = getPIPointWebId('marc-pi2016',tag1name);
webId2 = getPIPointWebId('marc-pi2016',tag2name);
webId3 = getPIPointWebId('marc-pi2016',tag3name);
values = getInterpolatedValues('1-Oct-2012','1-Nov-2012','1h', webId1, webId2, webId3);
51
The package rCurl is used to make the HTTP request and the package rJson is used to
convert the JSON string response into an R object.
All HTTP requests are made through the getHttpRequest method, which is called by the
getPIPointWebId and getInterpolatedValues.
Four HTTP GET requests are made. Three for getting the WebIds of the 3 PI Points. One
request for getting the interpolated values.
Using PI Web API Batch, it is possible to get the same results with one call only.
Nevertheless, it requires more time and effort in order to write the code.
This package allows you to edit the HTTP header on the getURL function. This is how the
Basic Authentication was added.
The ssl.verifypeer = FALSE was used due to the fact that the SSL certificate was self-signed. If
the SSL certificate is trusted by a trusted certificate authority, then this option wouldnt be
necessary.
The first thing to do is to connect to the PI SQL DAS, which will call the PI Server through PI OLEDB
Provider or PI OLEDB Enterprise. In this case, PI OLEDB Enterprise is used.
R
dbhandle <- odbcDriverConnect('Driver={PI ODBC Driver};Server=MARC-WEB-SQL;Trusted
Connection=Yes;Provider Type=PIOLEDBENT;Initial Catalog=NuGreen; Provider String={Data
Source=MARC-PI2016; Integrated Security=SSPI};')
Note: PI OLEDB Enterprise 2016 is still read-only. Therefore, only use this product if you are sure that
you wont need to write/modify anything on the PI System.
Then, you just need to execute the SQL query and store the values on variables.
52
R
values <- sqlQuery(dbhandle, 'SELECT ea.Name As Name, Format(i.Time, \'MMM-dd-yyyy
HH:mm:ss\'), i.Value
FROM [IntegratingPISystemAndR].[Asset].[ElementHierarchy] eh
INNER JOIN [IntegratingPISystemAndR].[Asset].[ElementAttribute] ea
ON ea.ElementID = eh.ElementID,
[IntegratingPISystemAndR].[Data].[ft_InterpolateRange] i
WHERE i.ElementAttributeID = ea.ID
AND i.StartTime = \'1-Oct-2012\'
AND i.EndTime = \'1-Nov-2012\'
AND i.TimeStep = \'1h\'
OPTION (FORCE ORDER, EMBED ERRORS)')
tag1name = 'FAC.OAK.Weather-Inside_Humidity-Val.PV';
tag2name = 'FAC.OAK.Weather-Inside_Temperature-Val.PV';
tag3name = 'FAC.OAK.Weather-Outside_Temperature-Val.PV';
n = (dim(values)[1])/3;
v1 = 1: n;
v2 = 1: n;
v3 = 1: n;
i=1;
j=1;
k=1;
m = 1;
while(m<3*n) {
if (values[[1]][[m]]=="OutsideTemperature")
{
v3[i] = values[[3]][[m]]
i=i+1
}
if (values[[1]][[m]]=="Inside Temperature")
{
v2[j] = values[[3]][[m]]
j=j+1
}
if (values[[1]][[m]]=="Inside Humidity")
{
v1[k] = values[[3]][[m]]
53
k=k+1
}
m <- m+1;
}
PI OLEDB Enterprise 2016 is still read-only. Therefore, only use this product if you are sure
that you wont need to write/modify anything on the PI System.
PI ODBC can also be used with the PI Integrator for Business Analytics.
54
One hot topic nowadays within the OSIsoft community is PI Coresight extensibility, which allow users
to develop almost anything they want to. As long as you know about AngularJS, HTML5, JavaScript,
there is a lot you can do in order to add value to your business through those custom symbols.
There are a lot of customers who has asked some guidelines about integrating the PI System with R
due to the power of R shown on previous chapters. With PI Coresight extensibility, custom symbols
could be developed in order to show nice graphics generated with PI data. In this sample, for a given
amount of attributes, a graphic showing the correlation among those attributes will be displayed. As
a prerequisite, a custom ASP.NET Web API web service needs to be created in order to achieve this
goal.
Figure 21 shows how the final custom symbol looks like for 3 attributes.
On the R.NET GitHub, there is a sample code for using R.NET in ASP.NET, which was used for me to get
started. I also recommend you to take a look at this Visual Studio project. Although the
SvgGraphicsContext.cs and SvgGraphicsDevice.cs files were initially copied from this repository, some
small changes were made in order to work better.
Note that the RDotNet.Graphics NuGet package library is not released yet. The library is still an alpha
version that works pretty well. Concerning the Svg library, the most recent version does not work well
with RDotNet.Graphics library. Please use version 2.0.0.0 instead.
Let's start looking at the QueryData class, which is the input of our main action:
public class QueryData
{
public int Width { get; set; }
public int Height { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public string Interval { get; set; }
public string[] Paths { get; set; }
public QueryData()
{
}
}
This means that the custom symbol will have to provide to our RESTful web service:
1)Width and Height of the symbol node
2)Time range that you want to analyze and generate the multi-correlation graphics
3)Paths from all the attributes
Let's see our controller and its unique action:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Http;
using RDotNet;
using RDotNet.Graphics;
using Svg;
56
using RMultWebService.Models;
using System.Web.Http.Cors;
using System.Web;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Configuration;
namespace RMultWebService.Controllers
{
public class CodeController : ApiController
{
private static REngine _engine = null;
private static SvgGraphicsDevice GraphicsDevice = null;
private static int lastWidth = -1;
private static readonly object _object = new object();
public CodeController()
{
if (_engine != null)
{
return;
}
[HttpPost]
public IHttpActionResult Execute(QueryData queryData)
{
Stopwatch watch = new Stopwatch();
watch.Start();
try
{
if ((queryData.Paths == null) || (queryData.Paths.Count() < 2))
57
{
throw new Exception("There should be at least 2 attributes within
the symbol.");
}
if (queryData.Paths.All(m => m.Substring(0, 2) == "af") == false)
{
throw new Exception("PI Points are not accepted");
}
IEnumerable<string> plots = null;
RApplication app = new RApplication();
app.GetPIData(queryData.Paths, queryData.StartTime, queryData.EndTime,
queryData.Interval);
lock (_object)
{
System.Threading.Thread.Sleep(1000);
if (lastWidth != queryData.Width)
{
GraphicsDevice = new SvgGraphicsDevice(new
SvgContextMapper(queryData.Width, queryData.Height, SvgUnitType.Pixel, null));
_engine.Install(GraphicsDevice);
lastWidth = queryData.Width;
}
app.GenerateGraphic(_engine);
plots = GraphicsDevice.GetImages().Select(RenderSvg);
}
return Ok(plots);
}
catch (Exception ex)
{
return BadRequest(ex.Message + "|\n" + ex.Source + "|\n" +
ex.StackTrace);
}
finally
{
watch.Stop();
}
}
58
image.Write(stream);
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var contents = reader.ReadToEnd();
return contents;
}
}
}
}
}
The main action checks if QueryData.Paths is valid. This collection should have at least two items,
which should all be attributes (each path string must start with "af"). Those paths are provided by the
PI Coresight web services.
Then, an RApplication object is instantiated and the GetPIData() method retrieves PI Data.
The next step is to send the data to R and generate the graphics. Nevertheless, in order to avoid
exceptions, this code snippet should be written inside a lock(_object). This will make sure that there
won't be two requests running inside this piece of code simultaneously. Note that in order to generate
the Svg content, the GraphicsDevice needs to be "installed" on the REngine. If the width is changed
when compared to the last request, a new GraphicsDevice needs to be created and replaced.
The RApplication class was taken from Chapter 9 of the White Paper and it was simplified as only one
function will be used. The content of this class is shown below:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using RDotNet;
using RDotNet.Graphics;
using System.IO;
using System.Diagnostics;
using OSIsoft.AF;
using OSIsoft.AF.Asset;
using OSIsoft.AF.Time;
using OSIsoft.AF.PI;
using System.Configuration;
59
namespace RMultWebService
{
public class RApplication
{
public RApplication()
{
PISystems piSystems = new PISystems();
string piSystemName = ConfigurationManager.AppSettings["piSystemName"];
PISystem piSystem = piSystems[piSystemName];
}
60
61
Other classes such as PIValue.cs, PIValues.cs and PIValuesList.cs were also copied from Chapter 9.
Our custom web service to generate SVG content through R is ready!
According to weather you are using 32-bit or 64-bit R binaries, you might want to set the "Enable 32Bit Application" setting to True on IIS of the web server hosting your custom web service. Please refer
to Figure 22 for more details.
62
On this chapter, the comments are about what is specific for this custom symbol. General PI Coresight
custom symbol development information can be found in many different resources, such as:
PI Coresight is developed on top of the AngularJS framework. The inject property of the definition
object makes some common AngularJS services available on the init function, including
the $http service for making RESTful web service calls. In this example. four services are injected:
$http this service is used for making a HTTP POST request against our custom web
service with R.NET and PI AF SDK.
$sce this service is used to show the content of the SVG on DOM.
$timeout this service is used to wait some seconds before running a function.
$document a wrapper for windows.document.
The links above will show more information directly on the AngularJS web site.
The code of the sym-rmult.js file is below:
(function (CS) {
var definition = {
typeName: 'rmult',
datasourceBehavior: CS.DatasourceBehaviors.Multiple,
iconUrl: 'Images/r-multcorr.svg',
inject: ['$http', '$sce', '$timeout', '$document'],
getDefaultConfig: function () {
return {
DataShape: 'Table',
63
Height: 400,
Width: 400,
Paths: null
};
},
init: init
};
var timer;
scope.resize = function (width, height) {
$timeout.cancel(timer);
timer = $timeout(function () {
scope.config.Width = width;
scope.config.Height = height;
scope.showGraphic = false;
scope.updateGraphic();
}, 1000);
}
scope.updateGraphic = function () {
if (scope.config.ElementsList == undefined) {
return;
}
64
postData.Interval = "1h";
postData.Width = scope.config.Width;
postData.Height = scope.config.Width;
postData.Paths = [];
for (var i = 0; i < scope.config.ElementsList.length; i++) {
postData.Paths.push(scope.config.ElementsList[i].Path);
}
if (postData.Paths.length < 2) {
alert('The symnol must have at least 2 attributes!');
return;
}
$http.post('http://marc-web-sql.marc.net:82/api/code',
postData).then(function (response) {
scope.plots = response.data;
scope.plot = scope.plots[0];
var currentElement = $document[0].getElementById(id)
var currentElementWrappedID = angular.element(currentElement);
currentElementWrappedID.height(scope.config.Width);
scope.config.Height = scope.config.Width;
scope.showGraphic = true;
})
}
65
The initial lines of the init function has the goal of changing the id of the symbol in
order to make it unique.
Yes, $http can also be used for making HTTP RESTful web service calls against PI Web
API.
While the symbol is loading the SVG content from the web service, a gif image from PI
Coresight is displayed by using the ng-show directive and checking the content of
the scope.showGraphic variable.
R.NET Graphics generate better graphics if the width is the same value of the height.
The data object received on the dataUpdate function is used only to get the paths of
the attributes and not the values themselves. Those are retrieved by the custom web
service using the paths array string and the time range.
Only AF Attributes work on this example. If a PI Point is added to the symbol, an
exception will be throw and the response will have a status code of 500. But it is not
difficult to update the code and make PI Points also compatible.
The paths from the attributes are stored on the scope.config.ElementsList.
66
12. CONCLUSIONS
Integrating the PI System and R might be very valuable for you and your enterprise as you will be
taking advantage of the R functions and libraries to plot figures with PI data.
This white paper has focused on .NET Framework and Java development but there are other systems/
languages which are able to achieve the same goals. PI Web API, a RESTful web service on top of the
PI System is compatible with major languages from the market.
This white paper comes with some sample applications that you can refer to in order to develop your
own custom application.
For suggestions or enhancements related to this white paper, please send me an e-mail directly
(mloeff@osisoft.com).
Finally, in order for us to start a conversation, I would like to encourage you to post on PI Developers
Club, a description of the projects related to this topic that you want or have implemented on your
enterprise. This would also be the right channel if you are not able to follow one step of this white
paper.
Happy coding!!
67
REFERENCES
[1] Um Ambiente Grfico para Facilitar Tarefas de Data Mining via Ferramenta R, Joel Frederico
Azevedo Costa
[2]Tutorial JRI- Wojtek
[3] http://rosuda.org/R/JavaGD/
[4] http://en.wikipedia.org/wiki/R_(programming_language)
[5] http://rdotnet.codeplex.com/
[6] PI Application Development Course Version 1.3c
[7] http://www.wikihow.com/Install-the-Java-Software-Development-Kit
[8] http://www.vogella.com/tutorials/Eclipse/article.html
[9] http://rforge.net/JRI/
68
REVISION HISTORY
11-Dec-12
27-May-14
05-Jun-14
15-Jul-16
69