Marcos Vainer Loeff

Web ID 2.0 client generation for Python and Java

Blog Post created by Marcos Vainer Loeff Employee on May 16, 2019

Introduction

 

PI Web API 2017 R2 is the first version that implements Web ID 2.0, which is great to improve the performance of your applications. Using this feature, your app doesn't need to make an HTTP request in order to get the Web ID of a PI System object since now you can generate it directly on the client side. If you want to learn more about Web ID 2.0 please refer to this material.

 

The purpose of this blog post is to show a simple way to generate Web IDs 2.0 in Python and Java.

 

Explaining the logic

 

Using the code below, you can generate Web IDs 2.0 Path only type with the following structure:

  • The first character will always be 'P'.
  • The second character will always be '1', which means that the Web ID version is 2.0.
  • Then we have the Marker with two characters which refers to the object's type.
  • Then we have the Owner Marker with 1 character (optional).
  • Finally, we have the Name Payload which encodes the following string PIObject.GetPath().Substring(2).ToUpperInvariant().

 

If you take a look at the Web ID 2.0 Specification Tables, you will realize that the Owner Marker needs to be defined only for some object types. The reason is that some object types could belong to different owners.

 

Let's take an example. We know that a PI Point will always belong to a PI Data Archive. Nevertheless, an AF Attribute could belong to an AF Element or to an AF Event Frame. This is why you don't need to define an Owner Marker for a PI Point but you do need to do it for an AF Attribute.

 

The beauty of the code below is that you can generate Web IDs for all objects using just a single method GenerateWebIdByPath method with 3 inputs:

  • Path of the PI System object
  • Class of the PI System object
  • Class of the owner of the PI System object (optional)

 

Here is a summary of what the code actually does:

  • Convert the object type to a Marker
  • Convert the owner object type to an Owner Marker
  • Validate if the Marker and Owner Marker are valid
  • Encode the Name Payload
  • Generate the Web ID 2.0

 

And finally the code...

 

Disclaimer:

This code could contain bugs and shouldn’t be used in production without extensive testing.

You agree that if you use any of the provided code in your own production code that you accept all ownership, risks, liabilities, and responsibilities associated with the performance, support, and maintenance of the code.

 


Licensing

 

Copyright 2019 OSIsoft, LLC.

 

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 

http://www.apache.org/licenses/LICENSE-2.0

 

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

 

To put this another way, if you have problems with this method, do NOT call Tech Support, nor create a case in my.OSIsoft.com. The only outlet for discussing issues with this code is here within the PI Developers Club forum.


Sample Python code

 

import base64



class WebIdHelper(object):
def __init__(self):
self.marker_owner = None
pass




def generate_web_id_by_path(self, path,  objct_type, owner_type=None):
self.validate_type_and_owner_type(objct_type, owner_type)
marker = self.get_marker(objct_type)
owner_marker = self.get_owner_marker(owner_type)
if path[0:2] == "\\\\":
path = path[2:]
encoded_path = self.encode_string(path.upper())
return "P1{}{}{}".format(marker, owner_marker, encoded_path)


def validate_type_and_owner_type(self, object_type, owner_type):
if isinstance(PIAttribute(), object_type):
if isinstance(PIElement(), owner_type) and isinstance(PIEventFrame(), owner_type):
raise WebIdException("PIAttribute owner type must be a PIElement or a PIEventFrame.")
elif isinstance(PIAttributeTemplate(), object_type):
if isinstance(PIElementTemplate(), owner_type):
raise WebIdException("PIElementTemplate owner type must be a PIElementTemplate.")
elif isinstance(PIEnumerationSet(), object_type) or isinstance(PIEnumerationValue(), object_type):
if isinstance(PIDataServer(), owner_type) == False and isinstance(PIAssetServer(), owner_type) == False:
raise  WebIdException("PIEnumerationSet and  PIEnumerationValue owner type must be a PIDataServer or PIAssetServer.")
elif isinstance(PITimeRule(), object_type):
if isinstance(PIAnalysis(), owner_type) and isinstance(PIAnalysisTemplate(), owner_type):
raise WebIdException("PITimeRule owner type must be a PIAnalysis and PIAnalysisTemplate.")


def get_owner_marker(self, owner_type):
if owner_type == None:
return ""
if isinstance(PIAssetServer(),owner_type):
self.marker_owner = "R"
elif isinstance(PIDataServer(), owner_type):
self.marker_owner = "D"
elif isinstance(PIAnalysis(), owner_type):
self.marker_owner = "X"
elif isinstance(PIAnalysisTemplate(), owner_type):
self.marker_owner = "T"
elif isinstance(PIElement(), owner_type):
self.marker_owner = "E"
if isinstance(PIElementTemplate(), owner_type):
self.marker_owner = "E"
elif isinstance(PIEventFrame(), owner_type):
self.marker_owner = "F"
return self.marker_owner


def get_marker(self, object_type):
marker = None


if isinstance(PIAnalysis(), object_type):
marker = "Xs"
elif isinstance(PIAnalysisCategory(), object_type):
marker = "XC"
elif isinstance(PIAnalysisTemplate(), object_type):
marker = "XT"
elif isinstance(PIAnalysisRule(), object_type):
marker = "XR"
elif isinstance(PIAnalysisRulePlugIn(), object_type):
marker = "XP"
elif isinstance(PIAttribute(), object_type):
marker = "Ab"
elif isinstance(PIAttributeCategory(), object_type):
marker = "AC"
elif isinstance(PIAttributeTemplate(), object_type):
marker = "AT"
elif isinstance(PIAssetDatabase(), object_type):
marker = "RD"
elif isinstance(PIAssetServer(), object_type):
marker = "RS"
elif isinstance(PIElement(), object_type):
marker = "Em"
elif isinstance(PIElementCategory(), object_type):
marker = "EC"
elif isinstance(PIElementTemplate(), object_type):
marker = "ET"
elif isinstance(PIEnumerationSet(), object_type):
marker = "MS"
elif isinstance(PIEnumerationValue(), object_type):
marker = "MV"
elif isinstance(PIEventFrame(), object_type):
marker = "Fm"
elif isinstance(PITimeRule(), object_type):
marker = "TR"
elif isinstance(PITimeRulePlugIn(), object_type):
marker = "TP"
elif isinstance(PISecurityIdentity(), object_type):
marker = "SI"
elif isinstance(PISecurityMapping(), object_type):
marker = "SM"
elif isinstance(PITable(), object_type):
marker = "Bl"
elif isinstance(PITableCategory(), object_type):
marker = "BC"
elif isinstance(PIPoint(), object_type):
marker = "DP"
elif isinstance(PIDataServer(), object_type):
marker = "DS"
elif isinstance(PIUnit(), object_type):
marker = "Ut"
elif isinstance(PIUnitClass(), object_type):
marker = "UC"
if (marker == None):
raise WebIdException("Invalid object type.")
return marker


def encode_string(self, value):
bytes = value.upper().encode('utf-8')
return self.encode(bytes)


def encode(self, value):
encoded = base64.b64encode(value).decode()
return encoded.strip('=').replace('+', '-').replace('/', '_')

 

Sample Java code

 

 

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;

public class WebIdHelper
{
    public WebIdInfo getWebIdInfo(String webId) throws WebIdException {
        return new WebIdInfo(webId);
    }

    public String generateWebIdByPath(String path, Class type, Class ownerType) throws WebIdException {
        validateTypeAndOwnerType(type, ownerType);
        String marker = getMarker(type);
        String ownerMarker = getOwnerMarker(ownerType);

        if (path.substring(0,2).equals("\\\\"))
        {
            path = path.substring(2, path.length());
        }
        String encodedPath = encode(path.toUpperCase());
        return ("P1" + marker + ownerMarker + encodedPath);
    }

    private void validateTypeAndOwnerType(Class type, Class ownerType) throws WebIdException {
        if (type == PIAttribute.class)
        {
            if ((ownerType != PIElement.class) && (ownerType != PIEventFrame.class))
            {
                throw new WebIdException("PIAttribte owner type must be a PIElement or a PIEventFrame.");
            }
        }
        else if (type == PIAttributeTemplate.class)
        {
            if ((ownerType != PIElementTemplate.class))
            {
                throw new WebIdException("PIElementTemplate owner type must be a PIElementTemplate.");
            }
        }
        else if ((type == PIEnumerationSet.class) || (type == PIEnumerationValue.class))
        {
            if ((ownerType != PIDataServer.class) && (ownerType != PIAssetServer.class))
            {
                throw new WebIdException("PIEnumerationSet and  PIEnumerationValue owner type must be a PIDataServer or PIAssetServer.");
            }
        }
        else if (type == PITimeRule.class)
        {
            if ((ownerType != PIAnalysis.class) && (ownerType != PIAnalysisTemplate.class))
            {
                throw new WebIdException("PITimeRule owner type must be a PIAnalysis and PIAnalysisTemplate.");
            }
        }
    }

    private String getOwnerMarker(Class ownerType)
    {
        String markerOwner = "";
        if (ownerType == null)
        {
            return markerOwner;
        }

        if (ownerType == PIAssetServer.class)
        {
            markerOwner = "R";
        }
        else if (ownerType == PIDataServer.class)
        {
            markerOwner = "D";
        }
        else if (ownerType == PIAnalysis.class)
        {
            markerOwner = "X";
        }
        else if (ownerType == PIAnalysisTemplate.class)
        {
            markerOwner = "T";
        }
        else if (ownerType == PIElement.class)
        {
            markerOwner = "E";
        }
        if (ownerType == PIElementTemplate.class)
        {
            markerOwner = "E";
        }
        else if (ownerType == PIEventFrame.class)
        {
            markerOwner = "F";
        }
        return markerOwner;
    }

    private String getMarker(Class type) throws WebIdException {
        String marker = null;
        if (type == PIAnalysis.class)
        {
            marker = "Xs";
        }
        else if (type == PIAnalysisCategory.class)
        {
            marker = "XC";
        }
        else if (type == PIAnalysisTemplate.class)
        {
            marker = "XT";
        }
        else if (type == PIAnalysisRule.class)
        {
            marker = "XR";
        }
        else if (type == PIAnalysisRulePlugIn.class)
        {
            marker = "XP";
        }
        else if (type == PIAttribute.class)
        {
            marker = "Ab";
        }
        else if (type == PIAttributeCategory.class)
        {
            marker = "AC";
        }
        else if (type == PIAttributeTemplate.class)
        {
            marker = "AT";
        }
        else if (type == PIAssetDatabase.class)
        {
            marker = "RD";
        }
        else if (type == PIAssetServer.class)
        {
            marker = "RS";
        }
        else if (type == PIElement.class)
        {
            marker = "Em";
        }
        else if (type == PIElementCategory.class)
        {
            marker = "EC";
        }
        else if (type == PIElementTemplate.class)
        {
            marker = "ET";
        }
        else if (type == PIEnumerationSet.class)
        {
            marker = "MS";
        }
        else if (type == PIEnumerationValue.class)
        {
            marker = "MV";
        }
        else if (type == PIEventFrame.class)
        {
            marker = "Fm";
        }
        else if (type == PITimeRule.class)
        {
            marker = "TR";
        }
        else if (type == PITimeRulePlugIn.class)
        {
            marker = "TP";
        }
        else if (type == PISecurityIdentity.class)
        {
            marker = "SI";
        }
        else if (type == PISecurityMapping.class)
        {
            marker = "SM";
        }
        else if (type == PITable.class)
        {
            marker = "Bl";
        }
        else if (type == PITableCategory.class)
        {
            marker = "BC";
        }
        else if (type == PIPoint.class)
        {
            marker = "DP";
        }
        else if (type == PIDataServer.class)
        {
            marker = "DS";
        }
        else if (type == PIUnit.class)
        {
            marker = "Ut";
        }
        else if (type == PIUnitClass.class)
        {
            marker = "UC";
        }
        if (marker == null)
        {
            throw new WebIdException("Invalid object type.");
        }

        return marker;
    }

    public static String encode(String value)
    {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        return encode(bytes);
    }

    public static String encode(byte[] bytes)
    {
        String value =  Base64.getEncoder().encodeToString(bytes);
        value = trimString(value, '=');
        return value.replace('+', '-').replace('/', '_');
    }

    public static String encode(UUID value)
    {
        byte[] bytes = value.toString().getBytes();
        return encode(bytes);
    }

    public static String trimString(String text, char trimBy) {
        int beginIndex = 0;
        int length = text.length();
        char[] textChar = text.toCharArray();

        while ((beginIndex < length) && (textChar[beginIndex] == trimBy)) {
            beginIndex++;
        }

        while ((beginIndex < length) && (textChar[length-1] == trimBy)) {
            length--;
        }

        if ((beginIndex > 0) || (length < text.length()))
        {
            return text.substring(beginIndex, length);
        }
        else
        {
            return  text;
        }
    }
}

 

Final Remarks and Conclusion

 

We hope that your Java and Python application will have a better performance by taking advantage of the Web ID 2.0 client-side generation!

 

Remember that PI Web API versions prior to 2017 R2 won't understand the Web IDs generated using this blog post!

Outcomes