Skip navigation
All Places > PI Developers Club > Blog > Authors csawyer

PI Developers Club

11 Posts authored by: csawyer Employee

One of the coolest things about Microsoft SQL Server in the last couple of years is how it has expanded from the confines of Windows Server and can now run on all three major desktop OSes, as well as sit in the cloud.

None of that expansion would have mattered much if downstream clients for SQL Server didn’t also expand their horizons to touch more platforms. And with Microsoft client tools for Linux and Mac, this is no longer an issue.

You can sneak PI Data through this mechanism

We can take advantage of Microsoft SQL Server and OSIsoft PI SQL by adding a linked server to SQL Server that forwards queries to PI Server. From there we can build SQL views which opens a portal into both the PI Data Archive and PI AF directly. You can also combine your own data stored in SQL with your real-time data. Downstream applications will see normal every day recordsets.

Here’s a screenshot where I’ve used this technique to pull data directly into Microsoft Excel for Mac. Not only is this data fresh, but I can refresh the query in my worksheet just like I would do in Excel for Windows. The connection from the worksheet is going straight to SQL Server.

ExcelForMac.png

Setup Steps

Setup PI SQL Data Access Server (RTQP Engine)

Make sure you’ve installed the PI SQL Data Access Server (RTQP) Engine which is in your PI Server 2018 (and later) install kit:

RTQP Install.png

Grab PI SQL Client

Next you need to get the PI SQL Client kit and install this on the instance where your SQL Server is. You can grab it from the OSIsoft Technical Support Downloads page.

Configure the PI SQL Client provider in Microsoft SQL Server Enterprise Manager

Hop over to SQL-EM and modify the linked server provider to ensure these options are switched on:

pisqlsetup.png

Create a Linked Server connection

By right-clicking on the Linked Servers folder in SQL-EM you can set up any number of linked server connections. Typically, you will want to set up one linked server connection per AF database. Here I’ve setup a connection to NuGreen:

linkedserversetup.png

Now the fun part: queries!

First let’s go with a basic type of query that finds all the pumps in the NuGreen database

Query

SELECT [ID]
   ,[Name]
   ,[Description]
   ,[Comment]
   ,[Revision]
   ,[HasChildren]
   ,[PrimaryPath]
   ,[Template]
   ,[Created]
   ,[CreatedBy]
   ,[Modified]
   ,[ModifiedBy]
   ,[TemplateID]
  FROM [PISERVER_TEST].[Master].[Element].[Element]
WHERE Name LIKE 'P%'
GO

Simple enough. This yields the following:

pumpssql.png

We can use a SQL Database to expose this as a view by wrapping this query with CREATE VIEW.

USE TEST
GO

CREATE VIEW REPORTING_PUMPS 
AS

SELECT [ID]
   ,[Name]
   ,[Description]
   ,[Comment]
   ,[Revision]
   ,[HasChildren]
   ,[PrimaryPath]
   ,[Template]
   ,[Created]
   ,[CreatedBy]
   ,[Modified]
   ,[ModifiedBy]
   ,[TemplateID]
  FROM [PISERVER_TEST].[Master].[Element].[Element]
WHERE Name LIKE 'P%'
GO

Now, when we select everything in the view we get:

reportingpumpsview.png

Perfect. Now that we have PI Server data we can pull this across to any application that can communicate to Microsoft SQL Server.

Importing PI Server data into Excel for Mac

Now that we have PI Server data exposed to Microsoft SQL Server it is fairly painless to connect this to downstream applications that can read recordsets from there. Let’s use this to connect the view we set up.

SQLServerODBCMac.png

Microsoft Excel can import remote a SQL datasets in the Data tab. From there you can select New Database Query and SQL Server ODBC.

odbcconnect.png

Insert your SQL Server credentials and authentication method and click Connect. A “Microsoft Query” window will then appear where you can enter a SQL statement to produce a recordset. It will follow the same syntax that you would use in SQL-EM.

From here I’ll select the contents of my view. Press Run to execute it on the server and inspect what comes back.

querywindow.png

Now you can press Return Data to deposit the results into your Excel worksheet. The connection to SQL Server is preserved in your worksheet when you save the Excel workbook. You can edit it and re-run the query by visiting the connections button that’s also on the Data ribbon.

connections.png

Data now refreshes on your terms in your Excel worksheet and your connection details are preserved between document openings.

Caveats

Read-only

Presently the restrictions that existed with PI OLDEDB Enterprise also apply to the latest PI SQL and Data Access Server. You cannot post data into your Asset Database via this connection type.

If you attempt to write, expect this error:

Msg 7390, Level 16, State 2, Line 35 The requested operation could not be performed because OLE DB provider “PISQLClient” for linked server “PISERVER_TEST” does not support the required transaction interface.

Got a bunch of Event Frames laying around?

 

Getting rid of old event frames in your database is now made much easier in AF SDK 2.10 with the new DeleteEventFrames() method that is hanging off the AFEventFrame class.

 

All you need to do is collect a list of event frame object IDs and pass it in to DeleteEventFrames(). You do this by performing an Event Frame Search.   The EventFrameSearch object supports returning the located event frames by Object ID-only, which you can use to mass delete them.

 

Why might I want to do this?

 

In some shops you might be creating Event Frames at a significant rate that has nothing to do with alarms and notifications but rather as part of an eventing process.  After those event frames run through your natural business process you may no longer have a desire to keep them around.  It would make sense then to setup a deletion schedule (once a week is recommended) to purge irrelevant data.

 

 

Make Sure To Use FindObjectIds() Where You Can

 

 

If you're doing regular mass deletions of objects it is far more efficient to limit your search returns to the AFObject IDs.  Not only does this reduce network traffic but the underlying database retrievals are going against well-indexed columns in the database which will result in fast returns.  By issuing mass deletes in page blocks (see the pageSize constant) you can loop through an unlimited number of event frames to purge until you have none left.  If your AFDatabase happens to undergo lots of writes and your batch deletes need  to happen when the server is busy, you can reduce the page size and introduce waits between each batch delete block to reduce the time the amount of locking in the underlying SQL database.

 

Observe:

using System;
using System.Linq;
using System.Collections.Generic;
using OSIsoft.AF;
using OSIsoft.AF.EventFrame;
using OSIsoft.AF.Search;

namespace MassEFDelete
{
    class Program
    {
        static void Main(string[] args)
        {

            const int pageSize = 200000;

            PISystems pi = new PISystems();
            PISystem sys = pi.DefaultPISystem;
            AFDatabase db = sys.Databases["Chris"];

            var cpu = db.Elements["CHRISMACAIR"];
            var machinetemplate = db.ElementTemplates["Macs"];

            string query = string.Format("Element:CHRISMACAIR");  // Every event frame where this AFElement is a factor
            var search = new AFEventFrameSearch(db, "CPU Search", query);

            //Use this if you really need to look in the event frames before deleting them,
            //else use the much faster FindObjectIds() method
            //var frames = search.FindEventFrames();  // Get every event

            List<Guid> ids = null;
            while ((ids = search.FindObjectIds(pageSize: pageSize).Take(pageSize).ToList()).Count > 0)
            {
                Console.WriteLine($"Deleting a batch of {ids} Event Frames..");
                AFEventFrame.DeleteEventFrames(sys, ids);
            }

            Console.WriteLine("Completed.");

        }
    }
}

 

Many of you have been frustrated with clearing event frames out in batch. Now you can conduct a lightweight search (fullload=false), grab the IDs out of the search and feed them into bulk delete all at once

I really need to see my CPU status all the time

How many times do you fish through Task Manager in Windows or pop open a terminal to run htop to reassure yourself that your CPU is running hot?  If you write code this is all the time.   For one project at OSIsoft I actually bought a DEC VT-420 dumb tube and a serial-to-USB adapter whose sole purpose was to run top as I was working because I needed to kill errant programs that often.  But that wasn't very green and it took up space on my desk.

 

I've found a better way. I have a Task Manager CPU display on my keyboard and I'm tracking my CPU% on the PI Server continuously.   Using Go*.

 

 

You can use any Web API Client library you want, but this was a good way to get a one-off daemon up and running quickly in a matter of minutes.

Introduction

 

We're excited to announce to PI Dev Club members that we now have a PI Web API Client Library for Go, the Google-sponsored programming language specifically designed around concurrent processing.

 

You can visit the GitHub repository of this library here.

 

Requirements

 

go1.9.7 or later

 

Installation

 

If you haven't already, install the Go software development kit.

 

Run this line to install the PI Web API Client for go

 

go get -u github.com/christoofar/gowebapi

 

Note: You don't need the Go SDK after you have compiled a go program and wish to deploy it somewhere.  Go creates self-reliant executable programs that compact dependent libraries inside them.

 

Getting Started

 

Here is a sample Go program for retrieving the links from the Web API home page.

 

Create a directory under %GOPATH% and let's call it webapitest. Then create a new code file with the name webapitest.go

This will print all the version numbers of your PI Web API server plugins. Replace the string literals {in braces} with the appropriate values for your environment.

 

// webapitest.go
package main

import (
    "context"
    "fmt"
    "log"

    pi "github.com/christoofar/gowebapi"
)

var cfg = pi.NewConfiguration()

var client *pi.APIClient
var auth context.Context

func Init() {
    cfg.BasePath = "https://{your web api server here}/piwebapi"

    auth = context.WithValue(context.Background(), pi.ContextBasicAuth, pi.BasicAuth{
        UserName: "{user name here}",
        Password: "{password here}",
    })

    client = pi.NewAPIClient(cfg)
}

func main() {
     Init()
    response, _, fail := client.SystemApi.SystemVersions(auth)
    if fail != nil {
        log.Fatal(fail)
    }

    fmt.Println("Here's all the plugin versions on PI Web API")
    for i := range response {
        fmt.Println(i, response[i].FullVersion)
    }
}

 

You can run the program by issuing the following commands

 

~/go/webapitest $ go build
~/go/webapitest $ ./webapitest

 

Your output should look something like this

 

~/go/webapitest $ ./webapitest
Here's all the plugin versions on PI Web API
OSIsoft.REST.Documentation 1.11.0.967
OSIsoft.REST.Services 1.11.0.967
OSIsoft.Search.SvcLib 1.8.0.3651
OSIsoft.PIDirectory 1.0.0.0
OSIsoft.REST.Core 1.11.0.967

 

Coding examples

 

There are some simple examples on how to start probing the PI Web API Client library over here.

 

Developing in Go

 

Golang programmers tend to develop using Visual Studio Code on Windows which has great golang support and is also available on MacOS and Linux.   There is great golang support available for emacs (configure emacs from scratch as a Go IDE) and vim as plugins which also give you function templates, IntelliSense, syntax checking, godoc (the documentation system for go), gofmt (code formatting/style) and support Delve, the debugger for the go language which cleanly handles the concept of go routines.

 

You can also build Go code with nothing but your web browser using the Go Playground.   This is a very handy tool where you can experiment with Go code snippets and compile and run them directly in a web browser, viewing the output.

 

Caveats

 

A WebID wrapper has been added to the library.  You can review the unit test code to see how you can create WebIDs from paths.

 

Final Thoughts

 

Most everyone's exposure with Go is minimal (including myself!).  But this language is expected to grow in popularity.   The reason: Go's awesome power to simplify concurrent programming is making it spread quickly, particularly within the realm of sensors and other lightweight devices.   Go code is also quite fast and produces programs that are lightweight yet powerful.

 

It's also a very simple programming language to learn (so simple you can become a Go programmer in 15 minutes if you have exposure to any other programming language).  Considering that there is also a rich library of data adapters written in Go, it made obvious sense to open a portal to the world of OSIsoft in golang.

 

Happy Gophering!

datanerd.jpg

 

A gopher is a euphemism Go programmers use to describe one another.

 

With the announcement of the PI Web API Client Library for the Go programming language I have hope that we can all broaden our understanding of concurrent programming.  Go isn't just the "programming language of the {date_year}".   It really is an exciting time to be coding, particularly with a programming language as simple to implement and understand as this one.

 

What is Go?

 

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, playground")
}

Go (known as golang) is a language funded by Google with a design goal of making concurrent programming much easier to write, debug and manage.  The principle designers at Google are Robert Griesemer, Rob Pike, and Ken Thompson formerly of AT&T and Bell Labs.   It's easier to explain what Go is by describing what it's not:

 

What isn't in Go

 

Objects

Yes, you can survive totally fine in a computer language these days with no objects.  It certainly lowers the barrier to new programmers who don't have the patience to memorize Design Patterns.   As a functional programming language that mimics C, avoiding holding on to state as much as possible is primarily the point.  So in the case of web server-based REST programming and other types of concurrent processing--avoiding holding on to state as much as possible is ideal and makes concurrent code easier to understand.

 

Threads

Well, threads as you've always known them to be.   Intel/ARM processor threads are used in Go programs as you would expect but go runtimes utilize a concept called a goroutine, which is a much more lightweight concept than a thread and they live within your processor's threads.   The main advantage of using a lightweight goroutine is massive scalability on a single server instance.   A Java service that might support 5,000 threads could theoretically scale to hundreds of thousands of threads when implemented in Go.   Some more benefits of goroutines:

  • You can run more goroutines on a typical system than you can threads.
  • Goroutines have growable segmented stacks.
  • Goroutines have a faster startup time than threads.
  • Goroutines come with built-in primitives to communicate safely between themselves (channels).
  • Goroutines allow you to avoid having to resort to mutex locking when sharing data structures.
  • Goroutines are multiplexed onto a small number of OS threads, rather than a 1:1 mapping.
  • You can write massively concurrent servers without having to resort to evented programming.

 

A process that normally would have to live on an expensive cloud VM instance or on a medium-sized server can be scaled down to an Arduino device or a much smaller VM instance.    This gives you an unparalleled amount of parallel power (pun intended), not only taking full advantage of all that hardware you paid for but it also affords you the capability to go cheaper down the hardware cost curve in future hardware.

 

An even more convincing selling point for Go is the power of race-condition debugging, which is difficult to do in nearly every evolved programming language.  The Data Race detector is built-in to Go and can pick up your memory conflict points in your code as you run a production workload through it.  To invoke the detector you just kick off your program with the go command using the -race option.

 

Anyone who has had to hunt down race conditions in .NET languages or C++ only to download loads of third-party tools to assist with locating offending race condition code would kill to have this feature built into the language.

 

Exception handling

One of the behaviors of Go that it definitely inherited from C is the concept of exception handling, or rather the lack of it.  Just like you must do in C and Visual Basic, in Go you will need to check returns from functions and handle error states immediately after calls that have a non-zero probability of failure.   The only thing you can really trust being available are memory cells, your local variables and the CPU.   Just about everything else you touch can fail (disk, network, http calls, etc).

 

To get around this though, Go supports multiple return variables from functions which is a very pleasing feature of the language.   A typical call to a function that might fail often looks like this:

 

var theData, err, numRecords := GetRecordsFromDatabase(userContext, sql)
if err != nil
{
     go myapp.PrintWarning("The database is down right now.  Contact Support. " + err)
     return
}

... // Begin processing records

 

What Go feels like to code in

 

The designers of Go definitely went on a shopping trip; starting with C and picking off concepts found in Pascal (the := assignment operator), inferred assignments from Javascript and C# using the same var keyword in both languages.  It also has pointers, breakouts to assembly, and the compiler condenses raw-metal binary executables that are freed from the need to host a JVM or a .NET runtime kit to have your programs launch.

 

One of the strongest benefits is that Go has garbage collection--a feature in hosted languages that need frameworks (Java and .NET).   So there aren't any calls to .free() and destructors are not necessary since there are no objects.  Instead, Go uses the defer keyword so you can run a cleanup routine when a function ends.   And unlike C there is no malloc() nonsense to worry about.   It's the fun of C without the items that make C frustrating.

 

The Go language is also surprisingly simple to wrap your brain around to the point that I am seeing people who have learned Go as their first programming language.  The Go code spec has a strict convention on formatting and gofmt comes with Go which can lint your code.   It's also customary to use godoc to heavily document what you're doing inside go routines.  Once again this comes with Go; no 3rd party tools are necessary to stylize your code.   The burden of code reviews that developers must do inside teams is greatly simplified thanks to these standards.

 

These standards combined with the lightweight thread power this design offers make it easy to understand why this language is taking off so rapidly and why Google invested in it.

 

Where Should I Start?

 

These are places that helped me get started in Go:

 

IDE Developing in Go

 

Golang programmers tend to develop using Visual Studio Code on Windows which has great golang support and is also available on MacOS and Linux.   There is great golang support available for emacs (configure emacs from scratch as a Go IDE) and vim as plugins which also give you function templates, code completion, syntax checking, godoc (the documentation system for go), gofmt (code formatting/style) and support Delve, the debugger for the go language which cleanly handles the concept of go routines.

 

You can also build Go code with nothing but your web browser using the Go Playground. This is a very handy tool where you can experiment with Go code snippets and compile and run them directly in a web browser, viewing the output.

 

Happy Gophering!

The 2.10 release of AF SDK unveiled DisplayDigits as a property you can investigate and set on an AF DataReference. Let’s do a quick investigation of what this property entails.

 

Further, there is a new .DisplayValue() method on the AFValue object for rendering single and double-precision floating point numbers.

 

What DisplayDigits Is

DisplayDigits is a setting that you can place on floating-point number tags to convey how much precision you wish to be displayed on-screen by downstream applications.

 

pse_example.png

Valid settings are any integer number from -20 up to 10.

 

What it’s used for

Most of your floating-point data will be expressed either in 32-bit or 64-bit floating point numbers. This allows for a large variety of decimal expression with varying degrees of precision. Controlling what precision you get back is what DisplayDigits is for.

Numerical precision

Consider the floating-point data types in the PI Data Archive…

TypeFloat16Float32Float64
MinimumZero of tag1.175494351 E-382.2250738585072014 E-308
MaximumSpan of tag3.402823466 E +381.7976931348623158 E +308
Exponent-8 bits11 bits
Mantissa-23 bits52 bits
Accuracyn/a7 digits15 digits

Floating point datatypes make a representation of a decimal number with relative increasing levels of precision given the number of bits used.1

Let’s experiment:

The SINUSOID tag on your Data Archive by default is stored as a 32-bit floating-point number. Let’s investigate what all the possible DisplayDigits settings exposed in AF SDK reveal.

First you will need to use an AF Database and set an attribute to the SINUSOID tag on your PI Data Archive. The DisplayDigits property hangs off the AFAttribute class and not off of PIPoint.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OSIsoft.AF;
using OSIsoft.AF.Asset;
using OSIsoft.AF.Data;
using OSIsoft.AF.PI;


namespace DisplayDigitsExample
{
    class Program
    {
        static void Main(string[] args)
        {


            // Let's get the server
            PISystems pisystems = new PISystems();


            // Get an injection well asset
            AFElement well = pisystems["CLSAF"].Databases["TransformedWells"]
                .Elements["Injection Wells By Contractor"]
                .Elements["Paisano Transmission And Service Inc"]
                .Elements["Site1.F100_IW01"];


            // Get the line pressure
            AFAttribute linepressure = well.Attributes["Line Pressure"];
            
            Console.WriteLine($"Line Pressure uses PI tag {linepressure.DataReference.ConfigString}");
            Console.WriteLine($"Present Value: {linepressure.GetValue().ValueAsSingle()}");
            Console.WriteLine($"Display Digits Setting: {linepressure.DisplayDigits}");


            // Display all the DisplayDigits settings for this PI value
            for (int i = 10; i >= -20; i--)
            {
                Console.WriteLine($"Value with DisplayDigits Set to {i}: {linepressure.GetValue().DisplayValue(i, null, true)}");
            }


            Console.ReadLine();


        }
    }
}

 

Here's the same example in VB.Net...

 

Imports OSIsoft.AF
Imports OSIsoft.AF.Data


Module Module1
    Sub Main()
        Dim piServers = New PISystems


        Dim wellsDB = piServers("CLSAF").Databases("TransformedWells")


        Dim well = wellsDB.Elements("Injection Wells By Contractor") _
            .Elements("Paisano Transmission And Service Inc") _
            .Elements("Site1.F100_IW01")


        Dim linePressure = well.Attributes("Line Pressure")


        Console.WriteLine("Line pressure uses PItag " & linePressure.DataReference.ConfigString)
        Console.WriteLine("Present value: " & linePressure.GetValue().ValueAsSingle())
        Console.WriteLine("Display Digits Setting: " & linePressure.DisplayDigits)


        For i = 10 To -20 Step -1
            Console.WriteLine($"Value with DisplayDigits Set to {i}: {linePressure.GetValue().DisplayValue(i, Nothing, True)}")
        Next
        Console.ReadLine()
    End Sub
End Module

 

This yields the following:

img

 

So what’s with the negative and positive DisplayDigits?

After you run some example code against floating point data you have you will notice how DisplayDigits applies zero-padding and rounding.

Positive DisplayDigits settings will force a floating point number to be returned with a fixed number of digits to the right of the decimal point, up to 10 and include padding zeroes (0) where necessary.

If the number is more significant than what you specificed then the number may be rounded or truncated. For instance, if the value stored is 63.90804 but DisplayDigits is set at 2, the number is returned back as 63.91

A negative DisplayDigits setting instead determines the number of significant digits to display, with no zero padding.  If a rounding occurs the number of digits displayed may be less than the setting.

Why do I see more digits when I inspect .Value than when I use -7 or -15 DisplayDigits?

Floating point types are inherently imprecise as it’s a reflection on how your CPU represents floating-point values, given that the binary representation of the number is likely to not be exact.

That’s why you should use the new DisplayValue() method that’s been added to the AFValue object.

Footnotes

1 The PI Data Archive uses 32-bit single precision and 64-bit double precision as well as 16-bit

Enhancing the Hello World example from Part 1

 

Now we're going to take the Line Pressure and Tubing Pressure AF Elements that were under the gas wells from our example database and convert those Elements into Attributes that live directly under the wells.  This is pretty easy!

 

 

The additions to the AF Transform XML file are straightforward.   First, the Measures template defines the two AF Elements where the values for each sensor are.   And these elements are always found underneath each well, so we can enhance the search for the wells to bring in the Line Pressure and Tubing Pressure child elements, like this:

 

...
<SearchShapes>
  <!-- Specify the elements and attributes of the search pattern that AF Transformer uses to search the source PI AF database -->
  <Shape ID="1001" Required="true" FilterMatchType="Any" ShapeWalkType="TopBottom">
    <ShapeElements>
      <ShapeElement ID="1" Required="true" FilterMatchType="Any" MaxDepthFromParent="1" IsEntryPoint="false">
        <Filters>
          <ElementFilter Category="" Template="Facility" Name="*" />
        </Filters>
        <Attributes>
          <ShapeAttribute ID="10" Required="true" FilterMatchType="Any">
            <Filters>
              <AttributeFilter Category="" Template="" Name="Contractor Name" />
            </Filters>
          </ShapeAttribute>
          <ShapeAttribute ID="11" Required="true" FilterMatchType="Any">
            <Filters>
              <AttributeFilter Category="" Template="" Name="Description" />
            </Filters>
          </ShapeAttribute>
        </Attributes>
        <ShapeElements>
          <!-- Line Pressure AF Element -->
          <ShapeElement ID="2" Require="true" FilterMatchType="Any" MaxDepthFromParent="1" IsEntryPoint="false">
            <Filters>
              <ElementFilter Category="" Template="Measures" Name="*PLN" />
            </Filters>
            <Attributes>
              <ShapeAttribute ID="20" Required="true" FilterMatchType="Any">
                <Filters>
                  <AttributeFilter Category="" Template="" Name="Value" />
                </Filters>
              </ShapeAttribute>
            </Attributes>
          </ShapeElement>
          <!-- End of Line Pressure AF Element -->
          <!-- Tubing Pressure AF Element -->
          <ShapeElement ID="3" Require="true" FilterMatchType="Any" MaxDepthFromParent="1" IsEntryPoint="false">
            <Filters>
              <ElementFilter Category="" Template="Measures" Name="*PTUB" />
            </Filters>
            <Attributes>
              <ShapeAttribute ID="30" Required="true" FilterMatchType="Any">
                <Filters>
                  <AttributeFilter Category="" Template="" Name="Value" />
                </Filters>
              </ShapeAttribute>
            </Attributes>
          </ShapeElement>
          <!-- End of Tubing Pressure AF Element -->
        </ShapeElements>
      </ShapeElement>
    </ShapeElements>
  </Shape>
</SearchShapes>
...

 

 

And then we just add the line pressure and tubing pressure attributes, referring back to the two elements:

 

...
<OutputElementShapes>
  <!-- Create and populate the asset model in the destination database -->
  <OutputElementShape ID="1003">
    <Elements>
      <Element Name="Injection Wells by Contractor" Guid="" ReadOnly="false" Template="">
        <Elements>
          <Element Name="[10.Value]" Guid="" Description="Contractor" ReadOnly="false" Template="">
            <Elements>
              <Element Name="[1.Name]">
                <Attributes>
                  <Attribute Name="Description" Value="[11.Value]" />
                  <Attribute Name="Line Pressure" Value="[20.DataReference]" />
                  <Attribute Name="Tubing Pressure" Value="[30.DataReference]" />
                </Attributes>
              </Element>
            </Elements>
          </Element>
        </Elements>
      </Element>
    </Elements>
  </OutputElementShape>
</OutputElementShapes>
...

 

Make sure when you're using PI Point data references (as well as AF table lookups) you use the .DataReference reference and not the .Value reference, otherwise you'll be pulling across the snapshot values of what those attributes are at the time you run AF Transformer.

Sometimes you wish your AF database was reorganized differently

 

I would hope your AF Database models follow a plant heirarchy and be nothing like tags in the Data Archive.  But you might not be so lucky.  Or, when using a PI Connector, AF Assets are created for you and they tend to follow the networking pattern for how the sensors assets are organized.  Often though, there are projects in your organization that would benefit greatly if your asset model were organized differently.  By reorganizing your AF database you could save yourself from expensive lookup queries or worse: traversing complicated parent/child chains using AF SDK or PI Web API.  It can make a lot more sense to get your asset model structured in a way that makes sense for whatever your present needs are.

 

Since AF SDK went public users have taken upon themselves to build AF SDK programs that do this sort of (re) "mapping".

 

AF Transformer helps you accomplish this remapping task without the need to build, compile and test C# code using AF SDK.  By editing an XML configuration file (with a good text editor ;-) and running the tool you can transform one or more AF databases into a new model that can greatly assist your projects and your downstream users who want to see assets in a way they understand.

 

 

Presented April 25, 2018 at PI World

 

Why might I use AF Transformer?

 

  • A downstream application is being built that needs to see/traverse your AF model in a way that would require a large number of lookups.  It might make a lot more sense to produce a new AF model that's pre-sorted to meet the needs of your downstream application--boosting performance and decreasing the amount of time it takes to develop.
  • You need to build a segmented AF model that limits the scope of assets to a particular group of users
  • You need to flatten, expand or pivot an AF model so it makes more sense to the various different business consumers in your organization

 

Getting Started - Let's pivot an AF database based on an attribute value

 

Inside the AF Transformer kit are examples that include two AF databases you can import.  I have an enhanced version of WellsDb.xml attached to this blog post (called WellsDBWithAFTable.xml) that includes an additional AF table of well maintenance contractors who are assigned to a well.  I have also added an attribute to link each well to a contractor and resolve the contractor's name.

 

2018-05-11_14-54-17.png

 

Import WellsDBWithAFTable.xml into a new AF Database in PI System Explorer (under File->Import From File...) I prefer to call it WellsDb, but you can name it whatever you like.

 

Next, create a second empty AF Database.  Let's call it TransformedWells.  This is where we're going to deposit the output from the transform.

 

Now, let's transform this.  Write an XML file with the following sections.  Be careful to update the <DataSource> and <Writers> tags to your correct source and target AF database host name and database names.  (CLSAF is the name of my own AF Server; which is likely not going to be the name of your own AF Server)

 

<?xml version="1.0" encoding="UTF-8"?>
<CASTDataSet xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <LogFilePath>c:\Program Files\PIPC\AFTransformer\AF Transformer Examples\example.log</LogFilePath>
   <StatInfo />
   <ProcessProperties>
      <!-- Default values for process properties are used -->
   </ProcessProperties>
   <Tasks>
      <Task Id="1000">
         <SearchShapes>
            <SearchShape Id="1001" ReaderId="1002" FilterMatchType="Any">
               <DataFilters />
            </SearchShape>
         </SearchShapes>
         <OutputElementShapes>
            <OutputElement Id="1003" />
         </OutputElementShapes>
         <TaskWriters>
            <TaskWriter Id="1004" />
         </TaskWriters>
      </Task>
   </Tasks>
   <DataSources>
      <DataSource MEFClass="AF" HighID="0" ID="1002">
         <Properties>
            <Properties Key="Host" Value="CLSAF" />
            <Properties Key="Database" Value="WellsDb" />
            <Properties Key="UserName" Value="" />
            <Properties Key="Password" Value="" />
            <Properties Key="DefaultPIServer" Value="" />
         </Properties>
      </DataSource>
   </DataSources>
   <Writers>
      <Writer MEFClass="AF" Enabled="true" Encoding="UTF8" ID="1004">
         <Properties>
            <Properties Key="Host" Value="CLSAF" />
            <Properties Key="Database" Value="TransformedWells" />
         </Properties>
      </Writer>
   </Writers>
   <SearchShapes>
      <!-- Specify the elements and attributes of the search pattern that AF Transformer uses to search the source PI AF database -->
      <Shape ID="1001" Required="true" FilterMatchType="Any" ShapeWalkType="TopBottom">
         <ShapeElements>
            <ShapeElement ID="1" Required="true" FilterMatchType="Any" MaxDepthFromParent="1" IsEntryPoint="false">
               <Filters>
                  <ElementFilter Category="" Template="Facility" Name="*" />
               </Filters>
               <Attributes>
                  <ShapeAttribute ID="10" Required="true" FilterMatchType="Any">
                     <Filters>
                        <AttributeFilter Category="" Template="" Name="Contractor Name" />
                     </Filters>
                  </ShapeAttribute>
                  <ShapeAttribute ID="11" Required="true" FilterMatchType="Any">
                     <Filters>
                        <AttributeFilter Category="" Template="" Name="Description" />
                     </Filters>
                  </ShapeAttribute>
               </Attributes>
            </ShapeElement>
         </ShapeElements>
      </Shape>
   </SearchShapes>
   <OutputElementShapes>
      <!-- Create and populate the asset model in the destination database -->
      <OutputElementShape ID="1003">
         <Elements>
            <!--Here is where the new AF Database model begins-->
            <Element Name="Injection Wells by Contractor" Guid="" ReadOnly="false" Template="">
               <Elements>
                  <Element Name="[10.Value]" Guid="" Description="Contractor" ReadOnly="false" Template="">
                     <Elements>
                        <Element Name="[1.Name]">
                           <Attributes>
                              <Attribute Name="Description" Value="[11.Value]" />
                           </Attributes>
                        </Element>
                     </Elements>
                  </Element>
               </Elements>
            </Element>
         </Elements>
      </OutputElementShape>
   </OutputElementShapes>
</CASTDataSet>

 

Now, let's run this.

 

In a command prompt window, call the utility directly with the /configxml="{path to your xml transform file here}" option.

 

2018-05-11_16-08-49.png

 

Go check your output

 

Take a look at the console output.  Make sure the AF Reader and the AF Writer in the console output both report that they've found their respective AF databases.  If so, the remaining part of the orchestration will complete and transpose the AF Objects.  Let's check the TransformedWells AF database to be sure.

 

2018-05-11_16-10-59.png

 

Yup, they're there.

 

This is the simplest example of AF Transformer working so that you grasp how to do grouping.  In Part 2, we will build off this example and convert the Line Pressure and Tubing Pressure AF Elements into Attributes that report directly on these transformed well elements.

 

>> Let's continue on to Part 2

OSIsoft has released a new watchface for PI World attendees!
20180419_102953.jpg

 

Here's how to get it on your SAMSUNG Gear S2 or S3 watch.   Start the SAMSUNG Gear store on your phone:

20180424_144259.jpg

Now jump to the Watch Face list...

20180424_144439.jpg

 

And search for "OSIsoft"...

20180424_144457.jpg

 

And now you can install it to your watch!

 

Screenshot_20180424-101715_Galaxy Apps.jpg

 

The watch face runs on TIZEN, SAMSUNG's newest mobile OS.

 

 

Web ID 2.0 Specification

 

These tables will help you encode and decode a PI Web API 2.0 Web ID so that you can both compose and deconstruct a Web ID yourself.   Visit How to Optimize your Applications for Web ID 2.0 to learn how the encoding works and how you can use Web ID to optimize your web application with PI Web API.

 

Web ID 2.0 first became available in PI Web API 2017 R2 (1.10 and later).   You can check your installed version by browsing to your PI Web API System endpoint  {yourwebapiserver}/piwebapi/system .

 

AFAnalysis

The Web ID for an AFAnalysis consists of the following data:

Full Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘F’

1

None

Version

‘1’

1

None

Marker

“Xs”

2

None

System ID

analysis.PISystem.ID

22

Urlencoded Guid

Analysis ID

analysis.ID

22

Urlencoded Guid

Name Payload

analysis.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

var

Urlencoded UTF8 String

 

ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘I’

1

None

Version

‘1’

1

None

Marker

“Xs”

2

None

System ID

analysis.PISystem.ID

22

Urlencoded Guid

Analysis ID

analysis.ID

22

Urlencoded Guid

 

Path Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘P’

1

None

Version

‘1’

1

None

Marker

“Xs”

2

None

Name Payload

analysis.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

var

Urlencoded UTF8 String

 

Local ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘L’

1

None

Version

‘1’

1

None

Marker

“Xs”

2

None

Analysis ID

analysis.ID

22

Urlencoded Guid

 

Default ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘D’

1

None

Version

‘1’

1

None

Marker

“Xs”

2

None

Analysis ID

analysis.ID

22

Urlencoded Guid

 

 

AFCategory for Analysis

The Web ID for an AFCategory, whose Identity is CategoryAnalysis, consists of the following data:

Full Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘F’

1

None

Version

‘1’

1

None

Marker

“XC”

2

None

System ID

category.PISystem.ID

22

Urlencoded Guid

Analysis Template

ID

category.ID

22

Urlencoded Guid

Name Payloadcategory.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘I’

1

None

Version

‘1’

1

None

Marker

“XC”

2

None

System ID

category.PISystem.ID

22

Urlencoded Guid

Analysis Template

ID

category.ID

22

Urlencoded Guid

 

Path Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘P’

1

None

Version

‘1’

1

None

Marker

“XC”

2

None

Name Payload

category.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

var

Urlencoded UTF8 String

 

Local ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘L’

1

None

Version

‘1’

1

None

Marker

“XC”

2

None

Analysis Template

ID

category.ID

22

Urlencoded Guid

 

Default ID Only Type

Field Name

Value

Encoded Width

Encoding Method

Type

‘D’

1

None

Version

‘1’

1

None

Marker

“XC”

2

None

Analysis Template

ID

category.ID

22

Urlencoded Guid

 

 

AFAnalysisTemplate

The Web ID for an AFAnalysisTemplate consists of the following data:

 

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“XT”2None
System ID

template.PISystem.ID

22Urlencoded Guid
Analysis TemplateID

template.ID

22Urlencoded Guid
Name Payload

template.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“XT”2None
System ID

template.PISystem.ID

22Urlencoded Guid
Analysis TemplateID

template.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“XT”2None
Name Payload

template.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“XT”2None
Analysis TemplateID

template.ID

22Urlencoded Guid
  1. 5.   Default ID Only Type
Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“XT”2None
Analysis TemplateIDtemplate.ID22Urlencoded Guid

 

AFAnalysisRule

The Web ID for an AFAnalysisRule consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“XR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
System IDanalysisRule.PISystem.ID22Urlencoded Guid
Owner IDanalysisRule.Analysis.ID or analysisRule.AnalysisTemplate.ID22Urlencoded Guid
Analysis Rule IDanalysisRule.ID22Urlencoded Guid
Name PayloadanalysisRule.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“XR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
System IDanalysisRule.PISystem.ID22Urlencoded Guid
Owner IDanalysisRule.Analysis.ID or analysisRule.AnalysisTemplate.ID22Urlencoded Guid
Analysis Rule IDanalysisRule.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“XR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Name PayloadanalysisRule.GetPath(AFEncodeType.Name,null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“XR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Owner IDanalysisRule.Analysis.ID oranalysisRule.AnalysisTemplate.ID22Urlencoded Guid
Analysis Rule IDanalysisRule.ID22Urlencoded Guid

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“XR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Owner IDanalysisRule.Analysis.ID oranalysisRule.AnalysisTemplate.ID22Urlencoded Guid
Analysis Rule IDanalysisRule.ID22Urlencoded Guid

 

AFPlugIn for Analysis Rule

The Web ID for an AFPlugIn, whose Identity is PlugInAnalysisRule, consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“XP”2None
System IDanalysisRulePlugIn.PISystem.ID22Urlencoded Guid
Event Frame IDanalysisRulePlugIn.ID22Urlencoded Guid
Name PayloadanalysisRulePlugIn.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“XP”2None
System IDanalysisRulePlugIn.PISystem.ID22Urlencoded Guid
Event Frame IDanalysisRulePlugIn.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“XP”2None
Name PayloadanalysisRulePlugIn.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“XP”2None
Event Frame IDanalysisRulePlugIn.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“XP”2None
Event Frame IDanalysisRulePlugIn.ID22Urlencoded Guid

 

AFAttribute

The Web ID for an AFAttribute consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Ab”2None
Base ElementMarker‘E’ if attr.Element is AFElement‘F’ if attr.Element is AFEventFrame‘N’ if attr.Element is AFNotification1None
System IDattr.PISystem.ID22Urlencoded Guid
Element IDattr.Element.ID22Urlencoded Guid
Attribute IDattr.ID22Urlencoded Guid
Name Payloadattr.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Ab”2None
Base ElementMarker‘E’ if attr.Element is AFElement‘F’ if attr.Element is AFEventFrame‘N’ if attr.Element is AFNotification1None
System IDattr.PISystem.ID22Urlencoded Guid
Element IDattr.Element.ID22Urlencoded Guid
Attribute IDattr.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Ab”2None
Base ElementMarker‘E’ if attr.Element is AFElement‘F’ if attr.Element is AFEventFrame‘N’ if attr.Element is AFNotification1None
Name Payloadattr.GetPath(AFEncodeType.Name,null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Ab”2None
Base ElementMarker‘E’ if attr.Element is AFElement‘F’ if attr.Element is AFEventFrame‘N’ if attr.Element is AFNotification1None
Element IDattr.Element.ID22Urlencoded Guid
Attribute IDattr.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Ab”2None
Base ElementMarker‘E’ if attr.Element is AFElement‘F’ if attr.Element is AFEventFrame‘N’ if attr.Element is AFNotification1None
Element IDattr.Element.ID22Urlencoded Guid
Attribute IDattr.ID22Urlencoded Guid

 

AFCategory for Attribute

The Web ID for an AFCategory, whose Identity is CategoryAttribute, consists of the following data: 

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“AC”2None
System IDcategory.PISystem.ID22Urlencoded Guid
Category IDcategory.ID22Urlencoded Guid
Name Payloadcategory.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“AC”2None
System IDcategory.PISystem.ID22Urlencoded Guid
Category IDcategory.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“AC”2None
Name Payloadcategory.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“AC”2None
Category IDcategory.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“AC”2None
Category IDcategory.ID22Urlencoded Guid

 

AFAttributeTemplate

The Web ID for an AFAttributeTemplate consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“AT”2None
ElementTemplate Marker‘E’Preserved here for legacy purposes1None
System IDattrTempl.PISystem.ID22Urlencoded Guid
ElementTemplate IDattrTempl.ElementTemplate.ID22Urlencoded Guid
AttributeTemplate IDattrTempl.ID22Urlencoded Guid
Name PayloadattrTempl.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“AT”2None
ElementTemplate Marker‘E’Preserved here for legacy purposes1None
System IDattrTempl.PISystem.ID22Urlencoded Guid
ElementTemplate IDattrTempl.ElementTemplate.ID22Urlencoded Guid
AttributeTemplate IDattrTempl.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“AT”2None
ElementTemplate Marker‘E’Preserved here for legacy purposes1None
Name Payload

attrTempl.GetPath(AFEncodeType.Name,null)

.Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“AT”2None
ElementTemplate Marker‘E’Preserved here for legacy purposes1None
ElementTemplate IDattrTempl.ElementTemplate.ID22Urlencoded Guid
AttributeTemplate IDattrTempl.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“AT”2None
ElementTemplate Marker‘E’Preserved here for legacy purposes1None
ElementTemplate IDattrTempl.ElementTemplate.ID22Urlencoded Guid
AttributeTemplate IDattrTempl.ID22Urlencoded Guid

 

AFDatabase

The Web ID for an AFDatabase consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“RD”2None
System IDdatabase.PISystem.ID22Urlencoded Guid
Database IDdatabase.ID22Urlencoded Guid
Name Payloaddatabase.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“RD”2None
System IDdatabase.PISystem.ID22Urlencoded Guid
Database IDdatabase.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“RD”2None
Name Payloaddatabase.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“RD”2None
Database IDdatabase.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“RD”2None
Database IDdatabase.ID22Urlencoded Guid

 
AFElement

The Web ID for an AFElement consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Em”2None
System IDelement.PISystem.ID22Urlencoded Guid
Element IDelement.ID22Urlencoded Guid
Name Payloadelement.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Em”2None
System IDelement.PISystem.ID22Urlencoded Guid
Element IDelement.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Em”2None
Name Payloadelement.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Em”2None
Element IDelement.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Em”2None
Element IDelement.ID22Urlencoded Guid

 

AFCategory for Element

The Web ID for an AFCategory, whose Identity is CategoryElement, consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“EC”2None
System IDcategory.PISystem.ID22Urlencoded Guid
Category IDcategory.ID22Urlencoded Guid
Name Payloadcategory.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“EC”2None
System IDcategory.PISystem.ID22Urlencoded Guid
Category IDcategory.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“EC”2None
Name Payloadcategory.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“EC”2None
Category IDcategory.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“EC”2None
Category IDcategory.ID22Urlencoded Guid

 

AFElementTemplate

The Web ID for an AFElementTemplate consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“ET”2None
System IDtemplate.PISystem.ID22Urlencoded Guid
ElementTemplate IDtemplate.ID22Urlencoded Guid
Name Payloadtemplate.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 
   ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“ET”2None
System IDtemplate.PISystem.ID22Urlencoded Guid
ElementTemplate IDtemplate.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“ET”2None
Name Payloadtemplate.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“ET”2None
ElementTemplate IDtemplate.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“ET”2None
ElementTemplate IDtemplate.ID22Urlencoded Guid

 

AFEnumerationSet

The Web ID for an AFEnumerationSet consists of the following data:

Full Type

Field NameValueEncodedWidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“MS”2None
Source Marker‘R’ if enumerationSet.PISystem != null‘D’ if enumerationSet.PIServer != null1None
Server IDenumerationSet.PISystem.ID (if AF)enumerationSet.PIServer.ID (if PI)22Urlencoded Guid
EnumerationSet IDenumerationSet.ID22Urlencoded Guid
Name PayloadenumerationSet.GetPath(AFEncodeType.Name,null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“MS”2None
Source Marker‘R’ if enumerationSet.PISystem != null‘D’ if enumerationSet.PIServer != null1None
Server IDenumerationSet.PISystem.ID (if AF)enumerationSet.PIServer.ID (if PI)22Urlencoded Guid
EnumerationSet IDenumerationSet.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncodedWidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“MS”2None
Source Marker‘R’ if enumerationSet.PISystem != null‘D’ if enumerationSet.PIServer != null1None
Name PayloadenumerationSet.GetPath(AFEncodeType.Name,null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

 

Local ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“MS”2None
Source Marker‘R’ if enumerationSet.PISystem != null‘D’ if enumerationSet.PIServer != null1None
EnumerationSet IDenumerationSet.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“MS”2None
Source Marker‘R’ if enumerationSet.PISystem != null‘D’ if enumerationSet.PIServer != null1None
EnumerationSet ID
  1. enumerationSet.ID
22Urlencoded Guid

 

 

AFEnumerationValue

The Web ID for an AFEnumerationValue consists of the following data: 
Full Type

Field NameValueEncodedWidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“MV”2None
Source Marker‘R’ if val.EnumerationSet.PISystem != null‘D’ if val.EnumerationSet.PIServer != null1None
Server IDval.EnumerationSet.PISystem.ID (if AF)val.EnumerationSet.PIServer.ID (if PI)22Urlencoded Guid
EnumerationSet IDval.EnumerationSet.ID22Urlencoded Guid
EnumerationValue IDval.ID22Urlencoded Guid
Name Payload(val.EnumerationSet.GetPath(AFEncodeType.Name, null).Substring(2) + '\\' + val.Name) .ToUpperInvariant()varUrlencoded UTF8String

 

ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“MV”2None
Source Marker‘R’ if val.EnumerationSet.PISystem != null‘D’ if val.EnumerationSet.PIServer != null1None
Server IDval.EnumerationSet.PISystem.ID (if AF)val.EnumerationSet.PIServer.ID (if PI)22Urlencoded Guid
EnumerationSet IDval.EnumerationSet.ID22Urlencoded Guid
EnumerationValue IDval.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncodedWidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“MV”2None
Source Marker‘R’ if val.EnumerationSet.PISystem != null‘D’ if val.EnumerationSet.PIServer != null1None
Name Payload

(val.EnumerationSet.GetPath(AFEncodeType.Name,null)

.Substring(2) + '\\' + val.Name) .ToUpperInvariant()

varUrlencoded UTF8String

 

Local ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“MV”2None
Source Marker‘R’ if val.EnumerationSet.PISystem != null‘D’ if val.EnumerationSet.PIServer != null1None
EnumerationSet IDval.EnumerationSet.ID22Urlencoded Guid
EnumerationValue IDval.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncodedWidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“MV”2None
Source Marker‘R’ if val.EnumerationSet.PISystem != null‘D’ if val.EnumerationSet.PIServer != null1None
EnumerationSet IDval.EnumerationSet.ID22Urlencoded Guid
EnumerationValue IDval.ID22Urlencoded Guid

 

 

AFEventFrame

The Web ID for an AFEventFrame consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Fm”2None
System IDeventFrame.PISystem.ID22Urlencoded Guid
Event Frame IDeventFrame.ID22Urlencoded Guid
Name PayloadeventFrame.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Fm”2None
System IDeventFrame.PISystem.ID22Urlencoded Guid
Event Frame IDeventFrame.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Fm”2None
Name PayloadeventFrame.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Fm”2None
Event Frame IDeventFrame.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Fm”2None
Event Frame IDeventFrame.ID22Urlencoded Guid

 

 

AFNotification

The Web ID for an AFNotification consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Nf”2None
System IDnotification.PISystem.ID22Urlencoded Guid
Element IDnotification.ID22Urlencoded Guid
Name Payloadnotification.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Nf”2None
System IDnotification.PISystem.ID22Urlencoded Guid
Element IDnotification.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Nf”2None
Name Payloadnotification.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Nf”2None
Element IDnotification.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Nf”2None
Element IDnotification.ID22Urlencoded Guid

 

 

AFNotificationTemplate

The Web ID for an AFNotificationTemplate consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“NT”2None
System IDtemplate.PISystem.ID22Urlencoded Guid
ElementTemplate IDtemplate.ID22Urlencoded Guid
Name Payloadtemplate.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“NT”2None
System IDtemplate.PISystem.ID22Urlencoded Guid
ElementTemplate IDtemplate.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“NT”2None
Name Payloadtemplate.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“NT”2None
ElementTemplate IDtemplate.ID22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“NT”2None
ElementTemplate IDtemplate.ID22Urlencoded Guid

 
AFTimeRule

The Web ID for an AFTimeRule consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“TR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
System IDtimeRule.PISystem.ID22Urlencoded Guid
Owner IDtimeRule.Analysis.ID or timeRule.AnalysisTemplate.ID22Urlencoded Guid
Time Rule IDtimeRule.ID22Urlencoded Guid
Name PayloadtimeRule.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“TR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
System IDtimeRule.PISystem.ID22Urlencoded Guid
Owner IDtimeRule.Analysis.ID or timeRule.AnalysisTemplate.ID22Urlencoded Guid
Time Rule IDtimeRule.ID22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“TR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Name Payload

timeRule.GetPath(AFEncodeType.Name,null)

.Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“TR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Owner IDtimeRule.Analysis.ID ortimeRule.AnalysisTemplate.ID22Urlencoded Guid
Time Rule IDtimeRule.ID22Urlencoded Guid

    

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“TR”2None
Owner Marker‘X’ if rule’s owner is AFAnalysis‘T’ if rule’s owner is AFAnalysisTemplate1None
Owner IDtimeRule.Analysis.ID ortimeRule.AnalysisTemplate.ID22Urlencoded Guid
Time Rule IDtimeRule.ID22Urlencoded Guid

 

 

AFPlugIn for Time Rule

The Web ID for an AFPlugIn, whose Identity is PlugInTimeRule, consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“TP”2None
System IDtimeRulePlugIn.PISystem.ID22Urlencoded Guid
Event Frame IDtimeRulePlugIn.ID22Urlencoded Guid
Name PayloadtimeRulePlugIn.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“TP”2None
System IDtimeRulePlugIn.PISystem.ID22Urlencoded Guid
Event Frame IDtimeRulePlugIn.ID22Urlencoded Guid

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“TP”2None
Name PayloadtimeRulePlugIn.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“TP”2None
Event Frame IDtimeRulePlugIn.ID22Urlencoded Guid

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“TP”2None
Event Frame IDtimeRulePlugIn.ID22Urlencoded Guid

 

 

AFSecurityIdentity

The Web ID for an AFSecurityIdentity consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“SI”2None
System IDsecurityIdentity.PISystem.ID22Urlencoded Guid
Security IdentityIDsecurityIdentity.ID22Urlencoded Guid
Name PayloadsecurityIdentity.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“SI”2None
System IDsecurityIdentity.PISystem.ID22Urlencoded Guid
Security IdentityIDsecurityIdentity.ID22Urlencoded Guid

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“SI”2None
Name PayloadsecurityIdentity.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“SI”2None
Security IdentityIDsecurityIdentity.ID22Urlencoded Guid

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“SI”2None
Security IdentityIDsecurityIdentity.ID22Urlencoded Guid

 

 

AFSecurityMapping

The Web ID for an AFSecurityMapping consists of the following data.

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“SM”2None
System IDsecurityMapping.PISystem.ID22Urlencoded Guid
SecurityMapping IDsecurityMapping.ID22Urlencoded Guid
Name PayloadsecurityMapping.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“SM”2None
System IDsecurityMapping.PISystem.ID22Urlencoded Guid
SecurityMapping IDsecurityMapping.ID22Urlencoded Guid

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“SM”2None
Name PayloadsecurityMapping.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8String

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“SM”2None
SecurityMapping IDsecurityMapping.ID22Urlencoded Guid

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“SM”2None
SecurityMapping IDsecurityMapping.ID22Urlencoded Guid

 
AFTable

The Web ID for an AFTable consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Bl”2None
System IDtable.PISystem.ID22Urlencoded Guid
Table IDtable.ID22Urlencoded Guid
Name Payloadtable.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 Strin

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Bl”2None
System IDtable.PISystem.ID22Urlencoded Guid
Table ID

table.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Bl”2None
Name Payload

table.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Bl”2None
Table ID

table.ID

22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Bl”2None
Table ID

table.ID

22Urlencoded Guid

 

 

AFCategory for Table

The Web ID for an AFCategory, whose Identity is CategoryTable, consists of the following data:

 

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“BC”2None
System ID

category.PISystem.ID

22Urlencoded Guid
Category ID

category.ID

22Urlencoded Guid
Name Payload

category.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“BC”2None
System ID

category.PISystem.ID

22Urlencoded Guid
Category ID

category.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“BC”2None
Name Payload

category.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“BC”2None
Category ID

category.ID

22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“BC”2None
Category ID

category.ID

22Urlencoded Guid

 

 

PIPoint

The Web ID for a PI Point consists of the following data:

 

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“DP”2None
Server ID

point.Server.ID

22Urlencoded Guid
Point ID

point.ID

6Urlencoded Int32
Name Payload

point.GetPath().Substring(2)

.ToUpperInvariant()
varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“DP”2None
Server ID

point.Server.ID

22Urlencoded Guid
Point ID

point.ID

6Urlencoded Int32

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“DP”2None
Name Payload

point.GetPath().Substring(2)

.ToUpperInvariant()
varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“DP”2None
Point ID

point.ID

6Urlencoded Int32

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“DP”2None
Point ID

point.ID

6Urlencoded Int32

 

 

PIServer

The Web ID for a PI Server consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“DS”2None
Server ID

server.ID

22Urlencoded Guid
Server Name

server.Name.ToUpperInvariant()

varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“DS”2None
Server ID

server.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“DS”2None
Server Name

server.Name.ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“DS”2None

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“DS”2None

 

 

PISystem

The Web ID for a PISystem consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“RS”2None
System ID

piSystem.ID

22Urlencoded Guid
Name Payload

piSystem.GetPath(AFEncodeType.Name,

null).Substring(2).ToUpperInvariant()
varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“RS”2None
System ID

piSystem.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“RS”2None
Name Payload

piSystem.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“RS”2None

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“RS”2None

 

 

UOM

The Web ID for a UOM consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“Ut”2None
System ID

uom.PISystem.ID

22Urlencoded Guid
UOM ID

uom.ID

22Urlencoded Guid
Name Payload

uom.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“Ut”2None
System ID

uom.PISystem.ID

22Urlencoded Guid
UOM ID

uom.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“Ut”2None
Name Payload

uom.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“Ut”2None
UOM ID

uom.ID

22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“Ut”2None
UOM ID

uom.ID

22Urlencoded Guid

 
UOMClass

The Web ID for a UOMClass consists of the following data:

Full Type

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker“UC”2None
System ID

uomClass.PISystem.ID

22Urlencoded Guid
UOM ID

uomClass.ID

22Urlencoded Guid
Name Payload

uomClass.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘I’1None
Version‘1’1None
Marker“UC”2None
System ID

uomClass.PISystem.ID

22Urlencoded Guid
UOM ID

uomClass.ID

22Urlencoded Guid

 

Path Only Type

Field NameValueEncoded WidthEncoding Method
Type‘P’1None
Version‘1’1None
Marker“UC”2None
Name Payload

uomClass.GetPath(AFEncodeType.Name, null).Substring(2).ToUpperInvariant()

varUrlencoded UTF8 String

 

Local ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘L’1None
Version‘1’1None
Marker“UC”2None
UOM ID

uomClass.ID

22Urlencoded Guid

 

Default ID Only Type

Field NameValueEncoded WidthEncoding Method
Type‘D’1None
Version‘1’1None
Marker“UC”2None
UOM ID

uomClass.ID

22

Urlencoded Guid

(If you're looking for the PI object encodings spec, visit this post)

 

Supported Versions

Web ID 2.0 first became available in PI Web API 2017 R2 (1.10 and later).   You can check your installed version by browsing to your PI Web API System endpoint  {yourwebapiserver}/piwebapi/system .

Video: Everything you wanted to know about WebID 2.0

 

What is Web ID?

Web ID is a fail-safe mechanism for retrieving artifcats from PI and AF servers while remaining safe for use in URLs. WebID URLs are resilient to changes of an object’s name or ID, which means you can store them and use them in your apps.

 

PI Web API uses Web ID as the main access mechanism to explore the objects in the PI system landscape.

 

If you already know the structure of your AF hierarchy, you can compose Web ID addresses yourself, sparing you the expense of consulting the query and search endpoints in PI Web API to determine what those Web IDs are.

 

How can I use Web ID to enhance my application?

With Web ID you can build direct links to storage areas in the PI System and cache those in your own application.   This is helpful for scenarios where you have a large number of objects to trade with PI Web API and you already know how the objects are modeled in the PI System.   They are also critical to efficient operations such as Batch requests where you need to perform a large amount of operations at once.

 

With Web ID you can also save on network bandwidth and transfer rates by composing shorter Web IDs where necessary, if server contexts aren’t expressly needed.

 

The Basics - How a Web ID is composed

A Web ID is a smart code that is comprised of several compound strings. The first four characters of a WebID determine how the remainder of the string is comprised and what object is being referenced.

 

Diagram of how a Web ID is composed

 

In the example here, a full version of a Web ID is being used to encode the ID of an AF Element.

 

The components of the Web ID in this instance are:

AF Element (Web ID Type: Full)

 

Field NameValueEncoded WidthEncoding Method
Type‘F’1None
Version‘1’1None
Marker‘Em’2None
System IDelement.PISystem.ID22Urlencoded Guid
Element IDelement.ID22Urlencoded Guid
Name Payloadelement.GetPath(AFEncodeType. Name, null).Substring(2).ToUpperInvariant()varUrlencoded UTF8 String

(Visit here for the full listing of supported object types and their encodings)

 

Web ID Types

The following Web ID types can be used to reduce the length of ID that you use to cache. You can request WebID types by adding ?webIDType={type} to any URL request in PI Web API, or posting any type of Web ID you wish to the PI Web API server. PI Web API will automatically figure out what type of Web ID you sent based on the formatting markers inside the ID.

 

Here are some examples:

Web ID TypeSampleElements Omitted
Full (default)F1EmDqD5loBNH0erqeqJodtALAh_fRZ2eH5xGWZgAVXQKgBgUkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx
?webIDType=IDOnlyI1EmDqD5loBNH0erqeqJodtALAh_fRZ2eH5xGWZgAVXQKgBgPath
?webIDType=PathOnlyP1EmUkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQxServer ID, Object ID
?webIDType=LocalIDOnlyL1Emh_fRZ2eH5xGWZgAVXQKgBgServer ID, Path
?webIDType=DefaultIDOnlyD1Emh_fRZ2eH5xGWZgAVXQKgBgServer ID, Path

Let’s decompose in code

The following code sample demonstrates how a full Web ID is parsed.

Example C# - Decomposing Web ID values

  using System;
  using System.Text;

  class WebIDExample
  {

  internal static string Encode(byte[] value)
  {
 string encoded = System.Convert.ToBase64String(value);
 return encoded.TrimEnd(new char[] { '=' }).Replace('+', '-').Replace('/', '_');
  }

  internal static string Encode(Guid value)
  {
 byte[] bytes = value.ToByteArray();
 return Encode(bytes);
  }

  internal static byte[] Decode(string value)
  {
 //Base 64 strings are in multiples of 4 bytes long.
 //This restores the = sign padding and changes the Uri-safe chars with the Base64 requirement
 StringBuilder decodestring = new StringBuilder(value.Replace('-','+').Replace('_','/'));
 int padneeded = value.Length % 4;
 for (int i = 0; i < padneeded; i++)
 {
 decodestring.Append('=');
 }

 byte[] bytes = System.Convert.FromBase64String(decodestring.ToString());
 return bytes;
  }

  internal static string DecodeString(string value)
  {
 return Encoding.UTF8.GetString(Decode(value));
  }

  internal static Guid DecodeGUID(string value)
  {
 byte[] guid = Decode(value);
 return new Guid(guid);
  }

  public static void Main()
  {

 // Given a Web ID of F1EmDqD5loBNH0erqeqJodtALAh_fRZ2eH5xGWZgAVXQKgBgUkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx,
 // let's decode the component parts.

 // F Full 1 Version Em AF Element RXhhbXBsZVN0cmluZw== Server
 // ID (GUID), string must be padded to decode
 // h/fRZ2eH5xGWZgAVXQKgBg== AF Object ID (GUID), change the _
 // to a / before decoding UkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx
 // Name (RESTUNIT\SAWYER\ELEMENT1)

 // Note: The decoded GUIDs are returned as a series of UTF8 bytechars, which are then
 // convertible into a GUID by printing the hex value of each byte.

 // Encode/decode basic string parts used in WebID
 Console.WriteLine("The F value of the WebID = Full");
 Console.WriteLine($"The value of RXhhbXBsZVN0cmluZw is {DecodeString("RXhhbXBsZVN0cmluZw")}");
 Console.WriteLine($"The value of DqD5loBNH0erqeqJodtALA is {DecodeGUID("DqD5loBNH0erqeqJodtALA").ToString()}");
 Console.WriteLine($"The value of h_fRZ2eH5xGWZgAVXQKgBg is {DecodeGUID("h_fRZ2eH5xGWZgAVXQKgBg").ToString()}");
 Console.WriteLine($"The value of UkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx is {DecodeString("UkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx").ToString()}");

  }

  }

 

Example JS - Decomposing Web ID values

//var btoa = require('btoa');
//var atob = require('atob');

function DecodeString(strDecode) {
  var decodestring = strDecode.replace('-', '+').replace('_', '/');
  var padneeded = decodestring.length % 4;
  for (var i = 0; i < padneeded; i++) {
  decodestring += '=';
  }

  return (atob(decodestring)).toString('utf8');;
}

function Base64ToArrayBuffer(base64) {
  var binary_string = atob(base64);
  var len = binary_string.length;
  var bytes = new Uint8Array(len);
  for (var i = 0; i < len; i++) {
  bytes[i] = binary_string.charCodeAt(i);
  }
  return bytes.buffer;
}

function DecodeGUID(strDecode) {
  var bytes = Base64ToArrayBuffer(strDecode);
  var uncodedbytes = new Uint8Array(bytes);

  var guidstr = "";

  for (var i = 3; i >= 0; i--) {
  if (uncodedbytes[i] < 17) {
  guidstr += "0" + uncodedbytes[i].toString(16);
  } else {
  guidstr += uncodedbytes[i].toString(16);
  }
  }
  guidstr += "-";
  if (uncodedbytes[5] < 17) {
  guidstr += "0" + uncodedbytes[5].toString(16);
  } else {
  guidstr += uncodedbytes[5].toString(16);
  }
  if (uncodedbytes[4] < 17) {
  guidstr += "0" + uncodedbytes[4].toString(16);
  } else {
  guidstr += uncodedbytes[4].toString(16);
  }
  guidstr += "-";
  if (uncodedbytes[7] < 17) {
  guidstr += "0" + uncodedbytes[7].toString(16);
  } else {
  guidstr += uncodedbytes[7].toString(16);
  }
  if (uncodedbytes[6] < 17) {
  guidstr += "0" + uncodedbytes[6].toString(16);
  } else {
  guidstr += uncodedbytes[6].toString(16);
  }
  guidstr += "-";
  if (uncodedbytes[8] < 17) {
  guidstr += "0" + uncodedbytes[8].toString(16);
  } else {
  guidstr += uncodedbytes[8].toString(16);
  }
  if (uncodedbytes[9] < 17) {
  guidstr += "0" + uncodedbytes[9].toString(16);
  } else {
  guidstr += uncodedbytes[9].toString(16);
  }
  guidstr += "-";
  for (i = 10; i < 16; i++) {
  if (uncodedbytes[i] < 17) {
  guidstr += "0" + uncodedbytes[i].toString(16);
  } else {
  guidstr += uncodedbytes[i].toString(16);
  }
  }

  return guidstr;
}


function ParseExample() {
  console.log("Taking the Full Web ID of " +
  "F1EmDqD5loBNH0erqeqJodtALAh_fRZ2eH5xGWZgAVXQKgBgUkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx\n");
  console.log("Let's break this into its constituent parts: ");
  console.log("F1 Full Web ID, Version 1");
  console.log("Em This is an AF Element");
  console.log("DqD5loBNH0erqeqJodtALA -> " + DecodeGUID("DqD5loBNH0erqeqJodtALA") + " Server ID");
  console.log("h_fRZ2eH5xGWZgAVXQKgBg -> " + DecodeGUID("h_fRZ2eH5xGWZgAVXQKgBg") + " AF Object ID");
  console.log("UkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx -> " + DecodeString("UkVTVFVOSVRcU0FXWUVSXEVMRU1FTlQx"));
}


ParseExample();

 

How the encoding works

 

Encoded fields are constructed mostly in two ways:

Encoded Strings (Base64)

UTF-8 strings designating names are encoded in Base64 . However, they do not include the padding characters required to decode them. This means you will need to follow this pattern:

  • Determine length of string
  • If the modulo of the string’s length is not evenly divisible by 4, then pad the string with equal signs ‘=’ until it is evenly divisible by four.
  • Once decoded into a binary array, convert the binary array to UTF-8 characters.

 

Encoded GUID/UUID (Base64)

How a GUID is composed

Universally Unique Identifiers (UUIDs) are 128-bit numbers that are used to uniquely identify any type of object as well as guarantee with an extremely high level of probability that randomly-generated numbers will not collide.

 

Web ID contains unqiue object identifiers that point to servers and resources in PI Asset Framework. Usually this will be a Server ID that identifies the AF Server where an Asset Framework object is based then GUID/UUID of an AF object itself; like an element, attribute, batch frame or template, etc.

 

Note about byte order - PI Web API uses Microsoft-formatted GUID numbers. Because they are expressed in Base64 strings, they will be 22 characters in length. In other environments such as Javascript or Java, UUIDs may be expressed with different byte orders.

 

Given a GUID expressed as DqD5loBNH0erqeqJodtALA, you must expand this string to DqD5loBNH0erqeqJodtALA== so that it is 24 characters long. Converting the individual bytes to hexadecimal notation and placing the bytes in the correct order should yield 96f9a00e-4d80-471f-aba9-ea89a1db402c as the server GUID.

 

 

 

 

For the full listing of supported PI object types and their Web ID encodings, visit here.

Filter Blog

By date: