Skip navigation
All Places > All Things PI - Ask, Discuss, Connect > AF Community Library > Blog > Authors Jerome Lefebvre

AF Community Library

5 Posts authored by: Jerome Lefebvre Employee

I recently wrote up a short introduction to using the URI builder Data Reference to create links to google maps. Here, I wanted to do the same to build a PI Coresight Ad Hoc display.


Ad Hoc displays are limited to display trends, so this particular example might not have a wide appeal, but I believe the techniques shown below should be more widely known.


In this example, we will show how to display the URL to open the following PI Coresight page.


We typically recommend to store the name of a PI Data Archive in a top level element and I did the same to store the name of the PI Coresight server information.


Let's now look at how I configured the URI Builder part by part.


First, the address uses the value of the address as: https://'\PI Coresight|Name'/Coresight/. The '\PI Coresight|Name' reference is what allows me to get the value of the PI Coresight server name.

I can see that this are working out well so far by looking at the preview view to the left of the ok button.



The Display field now simply the fixed parameter "/Display/AdHoc".


To build an Ad Hoc display, we need to get the path of the attribute we want to display in the Ad-Hoc display.

We can do this using a string builder reference. What we need is to build a string that contains the name of the AF server, the database, the path of all elements and finally the name of the attribute. In this case "temperature".


The string builder configuration is thus: \\;"%System%";\;"%Database%";\;%ElementPath%;|;Temperature;


We use this string builder data reference attribute in the URI builder as the following key/value pair.


The ".|" in ".|path" simply refers to accessing a child attribute of the URI builder attribute.


This makes a complete example, but to flesh things out more, we can add other parameters. Thus, I add in EndTime and StartTime as child attributes of my URI builder. (I also set them these child attributes as invisible as they do not need to typically seen).



I can then add them along side with other url parameters. Parameters such as kiosk mode and hidetoolbar.


This gives me a quick way to see this information on PI Coresight.

This is an ongoing series introducing the various code examples I and others have created in the goal of localizing the various AF example kits.


Part 0: URI Builder data reference in AF 2016

Part 1: Localizing the AF Example kits - Part 1 - Exporting a database

Part 2: This entry

Part 3: Localizing the AF Example kits - Part 3 - Reset to Template


Objects in AF tend to have configuration string, it is a way to serialize the object and is what allows you to export your favorite AF Template and share it with friends and family grateful coworkers.


As you progress in your path towards AF mastery, your knowledge of each of these configuration strings will surely grow and I will talk more about then in future entries to this blog series. This time, I want to highlight one configuration string that gave me pause at the beginning of this project to localize the AF Example Kits, the one underlying the AFVariableMap object.


The AFVariableMap object is what allows the analysis service to know where to save the output of expressions. That is, it what keeps track of the left and right side of the lines in a expression calculation.



When you look at the configuration string of an AFVariableMap, by say exporting a database to a XML file, you can see it looks like the following.



I can see the name of my variables, TotalFlowToday and ConstantNumber as well as my mapped attributes, TotalFlow and SixtyFour. The mapped attributes don’t just have their name stored, but their attribute id is stored. This is good as it allows you to rename the attributes, and the mapping will still work; if analysis tries an attribute lookup by name and fails, it will then attempt to do a look up via attribute id.

The issue is now, those ids are only valid in the database in which the elements were created. Those ids will be totally different if your were to recreate this configuration in any other database. Thus, if you export the element which contains this analysis and import it in a new database, everything will look correct as the lookup by attribute name will continue to work, but internally, these cached ids will be all wrong. This explains why renaming no longer preserves mapping for imported databases.


In AFSDK, there is happily a way to resolve this issue using the function AFAnalysisRule.RefreshConfigurationAndVariableMapping

It will look at the AFVariableMap object and update the GUID for whatever it now happens to be. To make use of it, it is quite simple, all you need to do is loop over all analysis in your freshly imported database and call this method on them.


foreach (var analysis in db.AnalysisTemplates)


The full code can be found here:

This is an ongoing series introducing the various code examples I and others have created in the goal of localizing the various AF example kits.


Part 0: URI Builder data reference in AF 2016

Part 1: Localizing the AF Example kits - Part 1 - Exporting a database

Part 2: Localizing the AF Example kits - Part 2 – Fixing analysis mapping after import


It is very common practice when working in PSE to make several changes in templates and then going to various element attributes and applying the “reset to template” to template to capture those changes. If you have only a few elements and attributes, this is not a problem. If you have a lot of elements and attributes, then you might learn the more advance technique of doing an attribute search.


You can then do a search for all the attributes that you do want to have updated. So, the easiest way to do so is to simply grab everything (with the limit of 1000 increase appropriately)

But, then you typically do not want to update values that might have been manually entered in an attribute, this usually the case for configuration items. Thus, you sort by configuration item and only select all elements that are not configuration items.

This can be error prone and you can end up resetting attributes that you did not wish to reset and lost valuable information.

Thus, it may be better to automate this reset to template feature with some AF SDK. There was already a blog post that detailed this procedure by Han Yong Lee. This code does not diverge to much from that post, but it also grabs child all child attributes.


Let say I have one child attribute, then the reset to template method is aptly called: ResetToTemplate.




Now, if you want to reset an attribute and all of its child attribute, you can apply this method and loop over all children attributes. As those could also have children attributes, you write a simple recursive method:


public static void resetAttribute(AFAttribute attr)
 foreach(AFAttribute childAttr in attr.Attributes)

Instead of applying these types of recursive methods, you would be better off using a FindAttribute method. But, as this is not a task that I do frequently or need to be particular quick, I took the liberty of writing the method in this way.

Now, let’s add in the check to see if an attribute is a configuration item that I do not want to update or if it is a PI Point that I want to update.


public static void resetAttribute(AFAttribute attr)
 if (!attr.IsConfigurationItem)
 if (attr.DataReference != null && attr.DataReferencePlugIn.Name == "PI Point")
 foreach(AFAttribute childAttr in attr.Attributes)

Now, it a regular scenario, I would need to be more careful about writing this method. AFAttribute.ResetToTemplate returns a Boolean that holds the success or failure of that operation. It can also raise events such as InvalidOperationException in the case a user and this attribute checked out. The AFDataReference.CreateConfig can raise more errors, such as in the case the specified PI Data Archive can’t be reached. But, for the use of the localization of AF Example kits, those errors do not tend to occur, thus, I have not coded against them.


To complete this script, I would then also recursively iterate over all elements in the database to grab all attributes.


The full code is found on GitHub: AF-Example-Kit-Utilities/ResetToTemplate at master · JeromeLefebvre/AF-Example-Kit-Utilities · GitHub 


This was worked done with Joon Hyung Im

This is an ongoing series introducing the various code examples I and others have created in the goal of localizing the various AF example kits.


Part 0: URI Builder data reference in AF 2016

Part 1: Exporting a database


When working to develop an AF Database and all that this contains, AF Tables, Elements, Templates, categories, UOMs, etc, you frequently need to share your work with others.

PI System Explorer offers many ways to do this. You can reach the export menu from the file menu to export your whole database.

Or you can be more selective and only pick out a single element to export.


And this is possible to do this manually every time you want to export your database to share with colleagues that may not have access to the same AF server as you do. Doing so manually can lead to errors, such as forgetting to include certain objects, such as UOMs. To avoid this, it is also possible to script this using AF SDK and using the PISystem.ExportXML methods. Here is an example that is used to export a database and several UOMS. This is the typical export that is done with the AF example kits.


The simplest example of using the method would be the following. To export the content of a database, the first thing you need to do is grab a reference to that database, in this case, I simply grab the default database. You can then pass this argument directly to the ExportXML. The other arguments are as follows, selecting an export mode: PIExportMode Enumeration. As this database is meant for users, using a different AF server, none of the IDs will match the system that I am using, thus the export mode I select is NoUniqueID. The next one is for convenience as it allows one to specify a path to where I want this export to be saved to.The next two are related to setting a start and end time for exporting event frames and the last an EventHandler to monitor the export process. I did not need the last three arguments, thus they are all set to null.


var db = pisystem.Databases.DefaultDatabase;
pisystem.ExportXml(db, PIExportMode.NoUniqueID, $"{directoryPath}\\OSIDemo_Utilities Management System_{version}.xml", null, null, null);

As further example, frequently you need to export UOMs that are used in the various elements that you have created in a database. To do so, one can simply grab the object, in this case a UOMClass object and use the export method on it. (Note that as there are no ways to build a UOMClasses object, we export each UOMClass to a different file.)


foreach (var UOMClassName in new string[] { "Power", "Energy Cost", "Volume Cost", "US Currency", "Energy per Volume" }) {
    var UOMClass = pisystem.UOMDatabase.UOMClasses[UOMClassName];
    pisystem.ExportXml(UOMClass, PIExportMode.NoUniqueID, $"{directoryPath}\\UOM_{UOMClass.Name}_{version}.xml", null, null, null);


The above code can be found at: AF-Example-Kit-Utilities/ExportKit at master · JeromeLefebvre/AF-Example-Kit-Utilities · GitHub


This work was done with Jason Jian-Hao Lu

I and several others in OSIsoft (chiefly among them Jason Jian-Hao Lu and Akane Takezaki) are currently working on localizing the AF Example kits and I promised a series of blog post to document the process.

To get myself started, please allow me to start with a post that is totally orthogonal to the whole process of translating this kit and simply highlight a new feature of AF 2016 I and Jason that we played with adding to the Asset Based PI Example Kit for Utilities Cost Management namely adding a URI Builder data reference.


The Utilities Cost Management uses Table Lookups to display latitude and longitude information of where a factory is located. This information was maybe hard to make use of directly into PI System Explorer in versions before 2016, but in 2016 we can now create URLs base on attributes. As we have latitude and longitude, the first thing that comes to mind is displaying this on a map, let's do so using Google Maps.


A typical Google Maps URL that uses latitude and longitude information looks like:,-122.1628,14z. The numbers that are listed, in order, are the latitude, longitude and the zoom level. In the kit there is already information regarding latitude and longitude, to hold the zoom level, the integer 14, we create a child attribute of the URI builder attribute that we are making. In our template, this looks like the following. The Zoom Level is a hidden configuration item as it is not typically required to be seen on clients.

Now the configuration of the Map URI (as seen on an element so that we may have data to fill the view):


The various parameters are as follows. The scheme is https and address is (or in my case: as there aren’t any other choices for the matter. The query field is used for URL parameters that appears URL in the form of: ?key=value&key2=value2 and fragment is for postfix of the form #id. As the Google Map URL uses neither of those features, they are both blank. We only have to fill the Path parameter. This is parameter is filled using string substitution for the value of the longitude, latitude and zoom level attributes. In the preview field at the bottom of the pane shows the exact same URL we started with, thus looks quite good. What we are left to do is to click on the URL and see our results in our browser of choice!