Dokumen - Tips - Af SDK 29 Getting Started Guide Pi Datalink Pi Datalink Server Pi Developeras
Dokumen - Tips - Af SDK 29 Getting Started Guide Pi Datalink Pi Datalink Server Pi Developeras
Introduction.............................................................................................................. 1
About OSIsoft developer technologies............................................................................................................ 2
AF SDK versus PI SDK......................................................................................................................................3
Where to find help........................................................................................................................................... 3
Configuration............................................................................................................ 5
Create the database for your PI system........................................................................................................... 5
Create an application structure....................................................................................................................... 6
Set the target framework................................................................................................................................ 6
Add AF SDK references to your project............................................................................................................7
Specify namespaces........................................................................................................................................ 7
Programming exercises.............................................................................................. 9
Lesson 1: Connect to PI Data Archive or PI AF database.................................................................................. 9
AF SDK basics............................................................................................................................................ 10
Connect to a PI Server or AF Database.......................................................................................................12
Lesson one exercises..................................................................................................................................13
Lesson 1 hints and tips............................................................................................................................... 15
Lesson 1 exercise solutions........................................................................................................................ 15
Lesson 2: Searching for assets........................................................................................................................17
Lesson 2 exercises..................................................................................................................................... 19
Lesson 2 hints and tips...............................................................................................................................20
Lesson 2 exercise solutions........................................................................................................................ 20
Lesson 3: Reading and writing data................................................................................................................22
Lesson 3 exercises..................................................................................................................................... 24
Lesson 3 hints and tips...............................................................................................................................28
Lesson 3 exercise solutions........................................................................................................................ 28
Lesson 4: Building an AF hierarchy.................................................................................................................31
Lesson 4 exercises......................................................................................................................................33
Lesson 4 hints and tips............................................................................................................................... 35
Lesson 4 exercise solutions........................................................................................................................ 36
Lesson 5: Working with AF event frames....................................................................................................... 41
Lesson 5 exercises......................................................................................................................................43
Lesson 5 hints and tips...............................................................................................................................44
Lesson 5 exercise solutions........................................................................................................................ 45
Conclusion............................................................................................................... 47
• AF SDK
Provides comprehensive, high-performance, Windows-based .NET programmatic access to
the PI System. AF SDK:
◦ Has the best performance of all PI development technologies.
◦ Has more available methods and options than any other technology.
◦ Is limited to Windows operating systems running .NET Framework.
• PI Web API
Enables operating system and device-independent programmatic access to the PI System
through a REST API, and is the recommended cross-platform offering for any operating
system that is able to use a modern web browser such as Google Chrome, Mozilla Firefox, or
Microsoft Edge. PI Web API:
◦ Is not dependent on a specific operating system or programming language.
◦ Has limited methods and options available compared with AF SDK.
• PI SQL Framework
Makes the data on PI Server available for querying as if it was on a relational database. PI
SQL Framework:
◦ Is compatible with other systems that use Structured Query Language (SQL).
◦ Has limited methods and options available compared with AF SDK (for example, setting
security on assets and creating units of measure).
◦ Is limited to read-only PI AF access (but can create and write to tags on your PI System).
There are more ways to access the PI System programmatically; however, the above three
methods describe the vast majority of use cases.
You might consider using the tools for Microsoft PowerShell. See the OSIsoft Tech Support page
PI Powershell (https://techsupport.osisoft.com/Documentation/PI-Powershell/title.html).
PowerShell cmdlets help with administration of PI Data Archive and PI Asset Framework
servers, and can be used in a variety of ways to create scripts for commonly needed
functionality or for bulk system management operations.
Documentation for these products (and others) can be found in: Live Library (https://
livelibrary.osisoft.com/).
Procedure
1. Clone a local copy of the GitHub repository.
The code for the exercises is located in GitHub:
https://github.com/osisoft/AF-SDK-Getting-Started-Guide
2. Start PI System Explorer and click File > Database
Procedure
1. If necessary, start Visual Studio.
2. Click File > New > Project
The New Project window displays.
3. Select Console Application, then enter a name for the project in the Name field.
For this example, enter PI_Connect as the project name. Visual Studio creates the project
and presents a program outline that includes the Main function. You augment this outline
with your own code.
Procedure
1. In Solution Explorer window, right click the program name. A tab opens with properties for
your application.
2. If necessary, click Application.
3. In the Assembly name list, select .NET Framework 4.5.2.
4. Close the tab.
Procedure
1. In Visual Studio, select Project > Add reference.
The Reference Manager window displays.
2. Click the Extensions node.
A list of custom assemblies displays.
3. Scroll the list until you see items from OSIsoft.
You can also enter a search term such as OSI in the Search Assemblies box.
4. Select the check box next to OSIsoft AFSDK version 4.0.0.0.
Be sure to select version OSIsoft AFSDK and not OSIsoft.PISDK. Also be sure to select
version 4 and not version 2.
5. Click OK.
The Reference Manager window closes and saves your changes.
The references you added using Reference Manager apply to the entire project, which
means that the referenced dynamic link libraries (DLLs or library files) are available to the
project. However, the referenced DLLs are quite large and contain lots of functionality that
your program might not use.
Specify namespaces
You must specify which namespaces you want to work with for a given DLL. For a given
application, you must add the appropriate using statements at the top of the program to
indicate the namespaces that the application will be using frequently.
Note:
When you run an application created with AF SDK, the application dynamically calls AF
SDK dynamic link libraries (DLLs); however, the application also requires some of the
services that are installed with AF SDK in addition to the DLLs. Therefore, you must
install PI System Explorer on any computer on which an AF SDK application is run.
Procedure
1. Using the Visual Studio editor, insert the following lines at the beginning of the program:
using OSIsoft.AF;
using OSIsoft.AF.Asset;
using OSIsoft.AF.UnitsOfMeasure;
For later class files or projects that you work on, some other popular "usings" are:
using OSIsoft.AF.Time;
using OSIsoft.AF.Data;
using OSIsoft.AF.EventFrame;
The using statements you add depend on which of the many namespaces in a given DLL you
want to have available to the program you are working on.
Introduction
For this lesson, you will access a PI AF database named Green Power Company, which has
already been configured on your local PI AF server. You will be asked to print to the console the
names and properties of various PI AF objects. You should familiarize yourself with the Green
Power Company PI AF database using PI System Explorer before starting the exercises.
AF SDK basics
AF SDK presents a hierarchical model of objects and collections that represent underlying PI
System features and concepts. Using AF SDK, you use the PISystem object as an abstraction for
your PI System.
The PISystem object is the top-level object used to represent a logical PI AF server. Each
PISystem can have one or more separate AFDatabase objects. Each AFDatabase object may
contain any number of child AFElement objects, which are sometimes referred to as assets.
Each AFElement object may have child AFElements or child AFAttributes. AFAttributes
of an AFElement may represent values from a data stream, table, or descriptive values (either
static or dynamically changing). The PISystem object is ideal for organizing assets that may be
grouped in hierarchies and classified using templates created to describe similar objects across
the database.
Note that the UOM (unit of measure) database is attached to a PISystem object. In addition,
only one UOM database exists in a PI System.
Namespaces
AF SDK is organized into several namespaces. The most common namespaces and their basic
functions are listed here. A block diagram of the PISystem Hierarchy (https://
techsupport.osisoft.com/Documentation/PI-AF-SDK/html/EB961F37-282A-43D2-8F8C-
F19CE07D9FA8.htm) is available in help.
• OSIsoft.AF
The AF namespace contains the main classes used by all the other namespaces within AF
SDK. It provides base class implementations and methods for connecting to PI AF servers.
The primary classes are PISystem and AFDatabase. Each PI System is composed of any
number of distinct databases. For more information see the OSIsoft Tech Support page
PISystem hierarchy (https://techsupport.osisoft.com/Documentation/PI-AF-SDK/html/
EB961F37-282A-43D2-8F8C-F19CE07D9FA8.htm).
• OSIsoft.AF.Asset
The Asset namespace provides a set of classes for representing assets within an
organization. It allows the creation of hierarchies of assets and their attributes. Additionally,
it provides features for dealing with common requirements such as remote data access,
unit-of-measure conversion, and defining and enforcing asset definitions. Templates for
assets ensure consistency among attributes. In the AF namespace, you will find AFElement,
AFAttribute, AFElementTemplates, and so on, along with their associated collections
(such as AFElements). In addition, AFValue, used to represent a time-series event, is
located in the AF namespace. For more information see the OSIsoft Tech Support page Asset
namespace (https://techsupport.osisoft.com/Documentation/PI-AF-SDK/html/
N_OSIsoft_AF_Asset.htm).
• OSIsoft.AF.Data
The Data namespace provides classes for obtaining rich data access (RDA) from assets
within an organization. Both historical and real-time data access are provided. Data access
across asset types allow for simpler architecture as well as helpful functionality such as
unit-of-measure conversions. For more information see the OSIsoft Tech Support page Data
namespace (https://techsupport.osisoft.com/Documentation/PI-AF-SDK/html/
N_OSIsoft_AF_Data.htm).
• OSIsoft.AF.PI
The PI namespace provides classes that are used to manage connections and access
information about PI Data Archive. It provides direct access to PI Data Archive and can be
used to find or edit PI points and read or write data directly to PI Data Archive. See the AF
SDK online reference for more information. For more information see the OSIsoft Tech
Support page AF.PI namespace (https://techsupport.osisoft.com/Documentation/PI-AF-
SDK/html/N_OSIsoft_AF_PI.htm).
• OSIsoft.AF.Time
The AF.Time namespace provides classes for performing time operations with the PI
System. It provides methods for converting time strings including abbreviations in the PI
time syntax (such as "*-5m") to an AFTime and converting .NET DateTime structures to and
from AF time structures. Time, time range, and time interval classes also facilitate accurate
representation across time zones and across daylight transitions. For more information see
the OSIsoft Tech Support page AF.Time namespace (https://techsupport.osisoft.com/
Documentation/PI-AF-SDK/html/N_OSIsoft_AF_Time.htm).
• OSIsoft.AF.EventFrame
The EventFrame namespace provides classes for reading and writing PI AF Event Frames.
These objects can be tied to PI AF elements and represent events that occur over a time
period, such as equipment downtime or abnormal behavior. For more information see the
OSIsoft Tech Support page EventFrame namespace (https://techsupport.osisoft.com/
Documentation/PI-AF-SDK/html/N_OSIsoft_AF_EventFrame.htm).
Namespaces are brought into scope in your application by adding the appropriate using
directives at the top of the class file. For example:
using OSIsoft.AF;
using OSIsoft.AF.Asset;
using OSIsoft.AF.Data;
After you obtain a reference to the PISystem, you can refer to its collection of AFDatabase
objects by using the Databases property of piSystem, as shown in the following code:
Notice that the object hierarchy corresponds very closely to the hierarchy you see in PI System
Explorer. Remember to use PI System Explorer as a guide for illustrating object relationships
within AF SDK.
Note that there is no explicit call to a Connect() method. AF SDK connects to the PI AF
database automatically as required. This automatic connection is called an implicit connection.
To make an explicit connection, you can call Connect() on a PISystem instance. Implicit and
explicit connections are discussed in more detail in the AF SDK Online Reference (https://
techsupport.osisoft.com/Documentation/PI-AF-SDK/html/a1616e71-
fb2e-40b1-9f14-299b2d9b269c.htm).
Example
static void PrintRootElements(AFDatabase database)
{
Console.WriteLine("Print Root Elements: {0}", database.Elements.Count);
foreach (AFElement element in database.Elements)
{
Console.WriteLine(" {0}", element.Name);
}
Console.WriteLine();
}
• Exercise 1
Create a method with the definition and arguments shown below. Incorporate the method
into your console application and print the name of each AFElementTemplate. For each
element template, also print the name of each AFCategory in its Categories collection.
void PrintElementTemplates( AFDatabase database )
• Exercise 3
Create a method with the definition and arguments shown below. Incorporate the method
into your console application and print the name of each unit of measure (UOM) and its
abbreviation under the UOMClass Energy.
void PrintEnergyUOMs( PISystem system )
• Exercise 4
Create a method with the definition and arguments shown below. Incorporate the method
into your console application and print the name of each AFEnumerationSet object. For
each set, print its list of states.
void PrintEnumerationSets( AFDatabase database )
Meter Status
0 - Good
1 - Bad
• Exercise 5
Create a method with the definition and arguments shown below. Incorporate the method
into your console application and print the name of each element category and attribute
category.
Attribute Categories
Building Info
Location
Time-Series Data
• Exercise 1
static void PrintElementTemplates(AFDatabase database)
{
Console.WriteLine("Print Element Templates");
AFNamedCollectionList<AFElementTemplate> elemTemplates =
database.ElementTemplates.FilterBy(typeof(AFElement));
foreach (AFElementTemplate elemTemp in elemTemplates)
{
Console.WriteLine("Name: {0}; Categories: {1}", elemTemp.Name,
elemTemp.CategoriesString);
}
Console.WriteLine();
}
• Exercise 2
static void PrintAttributeTemplates(AFDatabase database, string elemTempName)
{
Console.WriteLine("Print Attribute Templates for Element Template:
{0}", elemTempName);
AFElementTemplate elemTemp = database.ElementTemplates[elemTempName];
foreach (AFAttributeTemplate attrTemp in elemTemp.AttributeTemplates)
{
• Exercise 3
static void PrintEnergyUOMs(PISystem system)
{
Console.WriteLine("Print Energy UOMs");
UOMClass uomClass = system.UOMDatabase.UOMClasses["Energy"];
foreach (UOM uom in uomClass.UOMs)
{
Console.WriteLine("UOM: {0}, Abbreviation: {1}", uom.Name,
uom.Abbreviation);
}
Console.WriteLine();
}
Note that the UOMDatabase belongs to PISystem and is not exclusive to an AFDatabase.
• Exercise 4
static void PrintEnumerationSets(AFDatabase database)
{
Console.WriteLine("Print Enumeration Sets");
AFEnumerationSets enumSets = database.EnumerationSets;
foreach (AFEnumerationSet enumSet in enumSets)
{
Console.WriteLine(enumSet.Name);
foreach (AFEnumerationValue state in enumSet)
{
int stateValue = state.Value;
string stateName = state.Name;
Console.WriteLine("{0} - {1}", stateValue, stateName);
}
Console.WriteLine();
}
}
• Exercise 5
static void PrintCategories(AFDatabase database)
{
Console.WriteLine("Print Categories");
AFCategories elemCategories = database.ElementCategories;
AFCategories attrCategories = database.AttributeCategories;
Console.WriteLine("Element Categories");
foreach (AFCategory category in elemCategories)
{
Console.WriteLine(category.Name);
}
Console.WriteLine();
Console.WriteLine("Attribute Categories");
foreach (AFCategory category in attrCategories)
{
Console.WriteLine(category.Name);
}
Console.WriteLine();
}
Introduction
OSIsoft recommends that you reduce the search results to a minimum on the server, not on the
client. In other words, you should call AF SDK methods that allow the server to process the
search results instead of writing custom search logic in the client code.
Starting with PI AF 2016 (2.8), search query methods are available that process query strings
instead of methods for each type of search. For the exercises in this section, use the new
AFSearch methods such as the AFElementSearch class.
For this lesson, imagine you are a software developer for a power company. Your assignment is
to develop a client application that allows users to search for electric meters (PI AF Elements)
and attributes. When the application is complete, users should be able to search for meters by
name, element template, element category, and attribute values. The user should also be able to
search for meter attributes by name, element template, and attribute category.
Use the PI AF database named Green Power Company, which is located on your PI AF server.
You will be asked to write several methods that search for elements and attributes and print
the search results to the console. Before starting, it might help to familiarize yourself with the
Green Power Company database using PI System Explorer.
Example
The first example problem is shown here along with its solution. For this first problem, you
should implement the following methods inside Program2.cs and run the application with
the arguments provided for each of the following methods in Main().
static void FindMetersByName(AFDatabase database, string elementNameFilter)
{
Console.WriteLine("Find Meters by Name: {0}", elementNameFilter);
"ElementSearch", querystring);
foreach (AFElement element in elementquery.FindElements())
{
Console.WriteLine("Element: {0}, Template: {1}, Categories: {2}",
element.Name,
element.Template.Name,
element.CategoriesString);
}
Console.WriteLine();
Lesson 2 exercises
For each of the following exercises, your task is to add logic to each method. Add the methods
to your console application and test each one by writing output to the console.
• Exercise 1
Find all meters that were created from the provided element template and derived
templates. Print the names of each found meter in a list and their element template names.
Count the number of derived templates that were found.
void FindMetersByTemplate( AFDatabase database, string templateName)
• Exercise 2
Find all meters supplied by the provided substation. Print the names of each of the meters
that were found.
void FindMetersBySubstation( AFDatabase database, string substationLocation)
• Exercise 4
Find all attributes belonging to the Building Info attribute category that are part of the
provided element template. Print the number of attributes found.
void FindBuildingInfo( AFDatabase database, string templateName)
}
}
{
elementQuery.CacheTimeout = TimeSpan.FromMinutes(5);
int countNames = 0;
foreach (AFElement element in elementQuery.FindElements())
{
Console.Write("{0}{1}", countNames++ == 0 ? string.Empty : ", ",
element.Name);
}
Console.WriteLine();
}
}
int countNames = 0;
foreach (AFElement element in elementquery.FindElements())
{
Console.Write("{0}{1}", countNames++ ==
0 ? string.Empty : ", ", element.Name);
}
Console.WriteLine();
}
AFElementSearch search =
new AFElementSearch(database, "search", new[] { token });
Bulk calls
To retrieve data within a specified time range for many attributes, it is time-consuming to issue
a data call for each attribute, because many round trips over the network to the PI Data Archive
are required. In networked systems, latency delays can be costly.
You can reduce network latency (the number of network round trips) by using list calls in AF
SDK. First, you create a list of attributes to retrieve and store them in an AFAttributeList.
You then access the Data property on the AFAttributeList, which exposes data retrieval
methods for the entire attribute list. Now, you can make a single call to the server to retrieve
data for all of the attributes, as shown in the following code:
AFAttributeList attrList = AFAttribute.FindElementAttributes(
// argument list omitted for brevity
);
// Bulk call to get value at specified time for all found attributes
You might be wondering why the above example used AFAttributeList instead of
AFAttributes. The reason in that AFAttributes returns a list of attributes for an element,
whereas AFAttributeList returns a list of unrelated AFAttributes. For more information,
see AFAttributes (https://techsupport.osisoft.com/Documentation/PI-AF-SDK/html/
T_OSIsoft_AF_Asset_AFAttributes.htm) and AFAttributeList (https://techsupport.osisoft.com/
Documentation/PI-AF-SDK/html/T_OSIsoft_AF_Asset_AFAttributeList.htm).
The AFValue object contains two important properties:
Example
To read time-series data, you first obtain a reference to the AFAttribute, and then access its
Data property. The data property is an AFData object that exposes methods to retrieve time-
series data. In the example here, all of the archived values of an attribute for the last 10
minutes are retrieved.
AFTime startTime = new AFTime("*-10m");
AFTime endTime = new AFTime("*");
AFTimeRange timeRange = new AFTimeRange(startTime, endTime);
AFValues vals = attr.Data.RecordedValues(
timeRange: timeRange,
boundaryType: AFBoundaryType.Inside,
desiredUOM: null,
filterExpression: null,
includeFilteredValues: false);
Lesson 3 exercises
Your assignment is to develop a client application that allows users to read data from and write
data to the PI Data Archive.
You will use the PI AF database Green Power Company located on your local PI AF server. You
will be asked to write several methods to retrieve historical data from the PI Data Archive and
print the results to the console. You will also implement methods to write data. Before starting
the exercises, use PI System Explorer to familiarize yourself with the PI AF database.
For this lesson, implement the following methods inside Program3.cs and run the application
with the arguments provided for each of the following methods in Main().
• Exercise 1
Create a method to retrieve interpolated values for the Energy Usage attribute for the
specified meter element. The startTime and endTime should be in PI Time syntax. Print
the following information for each returned AFValue: Timestamp (Local time), Value in
kilowatt hours.
void PrintInterpolated(AFDatabase database, string meterName,
string startTime, string endTime, TimeSpan timeSpan)
• Exercise 2
Create a method to print the hourly time-weighted average for the Energy Usage attribute
for the specified meter element. The startTime and endTime should be in PI Time syntax
with time range larger than one hour. Print the following information for each returned
AFValue: Timestamp (Local time), Value in kilowatt hours.
void PrintHourlyAverage(AFDatabase database, string meterName,
string startTime, string endTime)
• Exercise 3
Create a method to retrieve the archived event at the given timestamp for the Energy Usage
attributes for all meters. The method should use the AFAttributeList object. Print the
following information for each meter: Meter name, Timestamp (Local time), Value in
kilowatt hour.
void PrintEnergyUsageAtTime(AFDatabase database, string timeStamp)
• Exercise 4
Create a method that retrieves the daily averages of the Energy Usage attribute for all
meters. The startTime and endTime should be in PI Time syntax. The method should use
the AFAttributeList object. Print the following information for each meter: Meter name,
Timestamp (Local time), Avg. Value in kilowatt hours.
void PrintDailyAverageEnergyUsage(AFDatabase database, string startTime,
string endTime)
• Exercise 5
Occasionally, the Energy Usage meter data is incorrect. The values for one meter actually
belong to another meter and vice versa. Therefore, implement the method from Exercise 4
which takes the name of two meters and start and end time in PI Time syntax. The method
should swap the Energy Usage attribute values between the two meters within the given
time range. Try to implement this with only two UpdateValues calls. What are some
tradeoffs when limiting the number of remote calls in this case? How would you ensure
there is no data loss?
void SwapValues(AFDatabase database, string meter1, string meter2,
string startTime, string endTime)
AFAttribute attr =
AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage",
database);
AFAttribute attr =
AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage",
database);
Console.WriteLine();
}
}
Console.WriteLine();
}
// NOTE: This method does not ensure that there is no data loss
// if there is failure.
// Persist the data first in case you need to rollback.
// Create new AFValues from the other meter and assign them to this meter
List<AFValue> valsToAdd1 = valsToRemove2.Select(v => new AFValue(attr1,
v.Value, v.Timestamp)).ToList();
List<AFValue> valsToAdd2 = valsToRemove1.Select(v => new AFValue(attr2,
v.Value, v.Timestamp)).ToList();
AFListData.UpdateValues(valsCombined, AFUpdateOption.Insert);
Console.WriteLine();
}
Introduction
When attempting to add an element to a database, you should always check to see if an element
with the same name already exists in the database before adding it. Attempting to add an
element that already exists in the database will generate an exception.
The following code shows one way to add an element named Meters to the database:
// Attempts to retrieve the element "Meters", and adds the element after
checking
// to see if it already exists
AFElement meters = database.Elements["Meters"]
if (meters == null)
meters = database.Elements.Add("Meters");
Alternatively, the previous operation could be written using a single line by using the C#
coalesce operator: ??, as shown here:
AFElement meters = database.Elements["Meters"] ?? database.Elements.Add("Meters")
Shown below is a simple example that shows how to create a new PI AF database, PI AF
element, and PI AF attribute, and then check in the changes. Refer to the Example 4 exercises
section for more detailed examples.
PISystem assetServer = new PISystems().DefaultPISystem;
database.CheckIn();
Example
The following example shows how to create an element template called FeederTemplate. The
example creates one attribute template called District which contains no data reference,
then creates another attribute template called Power. The default UOM is set to watt and the
Type to Single. The example also sets the data reference to PI Point.
See the following links for more information:
• Configuration of data references (https://livelibrary.osisoft.com/LiveLibrary/content/en/
server-v9/GUID-FBC4ED44-9448-4C17-95FE-BA55AA5CABF1)
• Units of measure in PI AF (https://livelibrary.osisoft.com/LiveLibrary/content/en/server-
v9/GUID-B6ABBFA8-22EE-42E3-84F3-BBAA638EFE58)
• PI point data reference (https://livelibrary.osisoft.com/LiveLibrary/content/en/server-v9/
GUID-9FA38FDC-4325-4222-BBBF-926DC1183685)
static void CreateElementTemplate(AFDatabase database)
{
string templateName = "FeederTemplate";
AFElementTemplate feederTemplate;
if (database.ElementTemplates.Contains(templateName))
return;
else
feederTemplate = database.ElementTemplates.Add(templateName);
AFAttributeTemplate cityAttributeTemplate =
feederTemplate.AttributeTemplates.Add("City");
cityattributeTemplate.Type = typeof(string);
power.DefaultUOM = database.assetServer.UOMDatabase.UOMs["watt"];
power.DataReferencePlugIn =
database.PISystem.DataReferencePlugIns["PI Point"];
database.CheckIn();
}
Lesson 4 exercises
• Exercise 1
Create an element called Feeders directly under the database root.
• Exercise 2
Create an element called Feeder001 under Feeders based off of the FeederTemplate
element template. Set the value of the City attribute to London. Set the ConfigString
property of the Power attribute such that the attribute's data source is the SINUSOID PI
Point.
• Exercise 3
Create a weak reference (https://livelibrary.osisoft.com/LiveLibrary/content/en/server-
v9/GUID-4318CCE5-2EBC-4239-BA04-8F0B1808BD53) between Feeder001 and the
Feeder001 element. The result should resemble the following structure:
Elements
- Configuration
- Feeders
- Feeder001
- Geographical Locations
- London
- Feeder001
- Meter001
- Meter002
- Meter003
- Meter004
+ Montreal
+ San Francisco
+ Meters
Bonus exercises
Create a replica of the AF Database Green Power Company.
7. Create the root element called Geographical Locations. Then, create three child
elements called London, Montreal, and San Francisco, based on the District template.
void CreateDistrictElements( AFDatabase database )
8. Add meter elements as weak references under the District elements, following the
reference database.
void CreateWeakReferences( AFDatabase database )
• Create an AFEnumerationSet
AFEnumerationSet bTypeEnum = database.EnumerationSets.Add("Building Type");
bTypeEnum.Add("Residential", 0);
bTypeEnum.Add("Business", 1);
• Add Overloads
When adding AFElement objects to an AFElements collection, several Add methods are
available. The methods can be roughly divided into two groups:
◦ Methods that accept a string denoting the new AFElement name and return a reference
to the newly created AFElement.
◦ Methods that accept a reference to an existing AFElement object and return void
A common use case for the second method is when adding an existing element as a weak
reference to a parent element as in the following:
AFReferenceType weakRefType = database.ReferenceTypes["Weak Reference"];
AFElement meters = database.Elements["Meters"];
AFElement buildingA = database.Elements["Meter Company"].Elements["Building
A"];
buildingA.Elements.Add(meters.Elements["Meter001"], weakRefType);
database.Elements.Add("Feeders");
database.CheckIn();
}
if (feeders.Elements.Contains("Feeder001")) return;
if (database.IsDirty)
database.CheckIn();
}
AFElement london =
database.Elements["Geographical Locations"].Elements["London"];
AFElement feeder0001 = database.Elements["Feeders"].Elements["Feeder001"];
if (!london.Elements.Contains(feeder0001))
london.Elements.Add(feeder0001, weakRefType);
database.CheckIn();
}
if (!database.EnumerationSets.Contains("Building Type"))
{
AFEnumerationSet bTypeEnum =
database.EnumerationSets.Add("Building Type");
bTypeEnum.Add("Residential", 0);
bTypeEnum.Add("Business", 1);
}
if (!database.EnumerationSets.Contains("Meter Status"))
{
AFEnumerationSet mStatusEnum =
database.EnumerationSets.Add("Meter Status");
mStatusEnum.Add("Good", 0);
mStatusEnum.Add("Bad", 1);
}
if (database.IsDirty)
database.CheckIn();
}
4. Create the PI AF Element Templates and their attribute templates. Set the properties and
categories based on the reference database.
In order to modularize the code and to limit each method to performing only one task, the
following solution was divided into four methods:
private static void CreateTemplates(AFDatabase database)
{
if (database == null) return;
AFElementTemplate meterBasicTemplate = CreateMeterBasicTemplate(database);
CreateMeterAdvancedTemplate(meterBasicTemplate);
CreateCityTemplate(database);
if (database.IsDirty)
database.CheckIn();
}
private static AFElementTemplate CreateMeterBasicTemplate(AFDatabase database)
{
AFElementTemplate meterBasicTemplate =
database.ElementTemplates["MeterBasic"];
if (meterBasicTemplate != null)
return meterBasicTemplate;
meterBasicTemplate = database.ElementTemplates.Add("MeterBasic");
meterBasicTemplate.Categories.Add(mEnergyE);
AFAttributeTemplate substationAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("Substation");
substationAttrTemp.Type = typeof(string);
AFAttributeTemplate usageLimitAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("Usage Limit");
usageLimitAttrTemp.Type = typeof(string);
usageLimitAttrTemp.DefaultUOM = uom;
AFAttributeTemplate buildingAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("Building");
buildingAttrTemp.Type = typeof(string);
buildingAttrTemp.Categories.Add(bInfoA);
AFAttributeTemplate bTypeAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("Building Type");
bTypeAttrTemp.TypeQualifier = bTypeNum;
bTypeAttrTemp.Categories.Add(bInfoA);
AFAttributeTemplate cityAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("City");
cityAttrTemp.Type = typeof(string);
cityAttrTemp.Categories.Add(locationA);
AFAttributeTemplate energyUsageAttrTemp =
meterBasicTemplate.AttributeTemplates.Add("Energy Usage");
energyUsageAttrTemp.Type = typeof(Single);
energyUsageAttrTemp.Categories.Add(tsDataA);
energyUsageAttrTemp.DefaultUOM = uom;
energyUsageAttrTemp.DataReferencePlugIn =
database.PISystem.DataReferencePlugIns["PI Point"];
energyUsageAttrTemp.ConfigString =
@"\\%@\Configuration|PIDataArchiveName%\%Element%.%Attribute%;UOM=kWh";
return meterBasicTemplate;
}
private static void CreateMeterAdvancedTemplate(AFElementTemplate
meterBasicTemplate)
{
AFDatabase database = meterBasicTemplate.Database;
AFElementTemplate meterAdvancedTemplate =
database.ElementTemplates["MeterAdvanced"];
if (meterAdvancedTemplate == null)
database.ElementTemplates.Add("MeterAdvanced");
AFCategory tsDataA =
database.AttributeCategories["Time-Series Data"];
AFEnumerationSet mStatusEnum =
database.EnumerationSets["Meter Status"];
meterAdvancedTemplate.BaseTemplate = meterBasicTemplate;
AFAttributeTemplate statusAttrTemp =
meterAdvancedTemplate.AttributeTemplates["Status"];
if (statusAttrTemp == null)
meterAdvancedTemplate.AttributeTemplates.Add("Status");
statusAttrTemp.TypeQualifier = mStatusEnum;
if (!statusAttrTemp.Categories.Contains(tsDataA))
statusAttrTemp.Categories.Add(tsDataA);
statusAttrTemp.DataReferencePlugIn =
database.PISystem.DataReferencePlugIns["PI Point"];
statusAttrTemp.ConfigString =
@"\\%@\Configuration|PIDataArchiveName%\%Element%.%Attribute%";
}
private static void CreateCityTemplate(AFDatabase database)
{
AFElementTemplate cityTemplate = database.ElementTemplates["City"];
if (cityTemplate != null)
return;
AFAttributeTemplate cityEnergyUsageAttrTemp =
cityTemplate.AttributeTemplates.Add("Energy Usage");
cityEnergyUsageAttrTemp.Type = typeof(Single);
cityEnergyUsageAttrTemp.DefaultUOM =
database.PISystem.UOMDatabase.UOMs["kilowatt hour"];
cityEnergyUsageAttrTemp.DataReferencePlugIn =
database.PISystem.DataReferencePlugIns["PI Point"];
cityEnergyUsageAttrTemp.ConfigString =
@"\\%@\Configuration|PIDataArchiveName%\%Element%.%Attribute%";
}
5. Create a root element called "Meters". Create individual meter elements from the
appropriate templates. Meter001-008 use MeterBasic. The rest use MeterAdvanced.
private static void CreateElements(AFDatabase database)
{
if (database == null) return;
// here we create the configuration element
// we do a small exception creating an attribute in this method.
AFElement configuration;
if (!database.Elements.Contains("Configuration"))
{
configuration = database.Elements.Add("Configuration");
AFAttribute name= configuration.Attributes.Add("PIDataArchiveName");
name.SetValue(new AFValue("PISRV01"));
}
if (database.IsDirty)
database.CheckIn();
}
6. Set the attribute values of the meter as appropriate based on the reference database. Only
do this for the first meter.
private static void SetAttributeValues(AFDatabase database)
{
if (database == null) return;
AFEnumerationValue bTypeValue =
database.EnumerationSets["Building Type"]["Residential"];
meter001.Attributes["Building Type"].SetValue(new AFValue(bTypeValue));
meter001.Attributes["City"].SetValue(new AFValue("London"));
}
7. Create the root element called Geographical Locations. Then, create three child
elements called London, Montreal, and San Francisco, based on the District template.
private static void CreateCityElements(AFDatabase database)
{
if (database == null) return;
if (!database.Elements.Contains("Geographical Locations"))
{
AFElement geoLocations = database.Elements.Add("Geographical Locations");
AFElementTemplate cityTemplate = database.ElementTemplates["City"];
geoLocations.Elements.Add("London", cityTemplate);
geoLocations.Elements.Add("Montreal", cityTemplate);
geoLocations.Elements.Add("San Francisco", cityTemplate);
}
if (database.IsDirty)
database.CheckIn();
}
8. Add meter elements as weak references under the District elements, following the reference
database.
private static void CreateWeakReferences(AFDatabase database)
{
if (database == null) return;
if (database.IsDirty)
database.CheckIn();
}
Introduction
An Event Frame represents a time period defined by a start time and end time. Each Event
Frame typically references one or more PI AF elements to associate the event with an asset. An
Event Frame can also have a collection of child Event Frames, which represent child events that
make up the larger event.
When creating AFEventFrame objects, the following properties should usually be set:
StartTime, EndTime, and PrimaryReferencedElement.
AFEventFrame ef = new AFEventFrame(database, "*", eventFrameTemplate);
ef.SetStartTime(startTime);
ef.SetEndTime(endTime);
ef.PrimaryReferencedElement = meter;
When creating Event Frame templates in code, you might be surprised to discover there is no
such object as AFEventFrameTemplate. Instead, an Event Frame template is a simply an
AFElementTemplate with its InstanceType property set to typeof(AFEventFrame).
AFElementTemplate eventFrameTemplate =
database.ElementTemplates["Name of Template"];
if (eventFrameTemplate == null)
AFElementTemplate eventFrameTemplate =
database.ElementTemplates.Add("Name of Template");
eventFrameTemplate.InstanceType = typeof(AFEventFrame);
AFEventFrameSearch eventFrameSearch =
new AFEventFrameSearch(database, "EventFrame Captures",
AFEventFrameSearchMode.ForwardFromStartTime,
startTime, queryString);
Because Event Frames typically represent historical time ranges, attribute values of event
frames typically do not change over time and can be cached to improve performance. In AF
SDK, you can call the CaptureValues() method on an event frame instance to capture and
save attribute values to the PI AF server for faster subsequent retrieval. Updating the start or
end time of the event frame will automatically recapture the values. For more information, see
the online AF SDK reference CaptureValues() (https://techsupport.osisoft.com/
Documentation/PI-AF-SDK/html/
M_OSIsoft_AF_EventFrame_AFEventFrame_CaptureValues.htm).
foreach (AFEventFrame item in eventFrameSearch.FindEventFrams())
{
item.CaptureValues();
}
Lesson 5 exercises
Your next task is to develop an energy reporting system for the PI AF database Green Power
Company. You should familiarize yourself with the PI AF database using PI System Explorer
before starting the exercise.
The Green Power Company database contains 12 electric meters deployed to different
buildings. Your task is to create PI AF Event Frames to track average energy usage per day. Each
PI AF Event Frame is created using a PI AF Event Frame Template. The template should have
one attribute configured to report the average of the Energy Usage attribute of a meter over the
event frame time range.
Please implement the following methods inside Program5.cs and run the application with the
arguments provided for each of the following methods in Main().
For each of the following exercises, your task is to add the logic to each method. Add the
methods to your console application and test each one by writing output to the console.
• Exercise 1
Create an AF Event Frame template called Daily Usage. Event frame names created from
this template should include the event frame template name, referenced element name, and
start date. For example: Usage Tracker-Meter002-2016-02-01 00:00:00.
Create a PI AF Attribute Template under the event frame template called Average Energy
Usage. Configure the AFAttributeTemplate properties such that the event frame
attribute reports the average value of the referenced Energy Usage attribute over the event
frame time range. See Lesson 5 hints and tips for an example configuration string to use.
AFElementTemplate CreateEventFrameTemplate(AFDatabase database)
• Exercise 2
For each meter and for each day within February 1 - 5, 2016, create an AFEventFrame based
off of the "Daily Usage" template. Set the PrimaryReferencedElement to the appropriate
meter.
• Exercise 3
Save the value of Average Energy Usage attribute to the PI AF server for each event frame
using CaptureValues() on the event frame instance. You will first need to find all event
frames using the AFEventFrameSearch.FindEventFrames() method.
void CaptureValues(AFDatabase database, AFElementTemplate eventFrameTemplate)
• Exercise 4
Find all of the event frames referencing Meter003. Use the
AFEventFrameSearch.FindEventFrames() method. Then print to the console the event
frame name, primary referenced element name, and value of the Average Energy Usage
attribute.
void PrintReport(AFDatabase database, AFElementTemplate eventFrameTemplate)
To configure the event frame attribute to report an average, set the data reference to a PI Point
DR. Use the following ConfigString:
.\Elements[.]|Energy Usage;TimeRangeMethod=Average
The ConfigString shown above configures the Average Energy Usage attribute to report the
average of the primary referenced element's Energy Usage over the event frame's time range.
AFAttributeTemplate usage =
eventFrameTemplate.AttributeTemplates.Add("Average Energy Usage");
usage.Type = typeof(Single);
usage.DataReferencePlugIn = AFDataReference.GetPIPointDataReference();
usage.ConfigString = @".\Elements[.]|Energy Usage;TimeRangeMethod=Average";
usage.DefaultUOM = database.PISystem.UOMDatabase.UOMs["kilowatt hour"];
if (database.IsDirty)
database.CheckIn();
return eventFrameTemplate;
}
if (database.IsDirty)
database.CheckIn();
}
if (database.IsDirty)
database.CheckIn();
}
}