1. PI Module Database


The PI Module Database provides a set of high-level server databases exposed through the PI-SDK object model. The databases and SDK object model are abstract enough to cover a wide range of applications, yet not so abstract as to impede application development.

There are a few key points that make the PI Module Database (PI MDB) the database of choice for your PI applications:

This document is an overview of the PI Module Database. The PI-SDK References has detailed interface documentation. Where appropriate, the online help version of this document provides hyperlinks to the specific object references.

  1. What's a Module?

The PI Module Database, as the name implies, stores modules. What is a Module? There are many definitions of the term "module," but the following is the best definition in the context of the PI Module Database:

"A module is a standardized, often interchangeable component of a system or construction that is designed for easy assembly or flexible use."

  1. Standardized

Standardization comes with the nature of API's and SDK's. Specifically ActiveX based SDK's force standardizations--any specific Object, in this case a Module, must support identical Properties and Methods. That is, a Module, no matter where or how it is used always has the same Properties and Methods. Properties are specific physical attributes; methods are capabilities or functions it can perform. For example, every module has a Name property.

  1. Interchangeable

Interchangeability of a module can be viewed two ways. First, a module is flexible--it can be used to represent many different things. Second, an instance of a module can be used in more than one place. The module database is hierarchical, much like a file system directory structure. A specific module can be referenced in more than one location in the hierarchy.

  1. Component of a System

The module database is a hierarchical collection of many modules. Modules can contain other modules. A module is a building block that is used to construct systems.

  1. Easy Assembly or Flexible Use

The Microsoft ActiveX approach allows for easy programmatic access from any programming language. A set of ActiveX Module Database Edit controls and an editor (PIMDBEditor) make for easier application development.

  1. Creating a Module

A goal of the PI Module Database is to allow easier access to PI time-series data. A common problem with the PI Archive is finding related points to use in an application. For example, PID control loops involve several PI Points. A point is required for each loop attribute. A typical PID loop is shown in the following PI ProcessBook display:

There are seven PI Points required for the loop "tic-104": process variable, set point, output, proportional constant, integral constant, derivative, and mode. This loop can easily be represented in a module. The following example will use Visual Basic to build the module.

  1. Visual Basic Example

This example uses the PI-SDK to build a module for the control loop tic-104. The example assumes some general knowledge of the PI-SDK and Visual Basic. The main entry point to the PI-SDK and the PI databases is the PI-SDK Server object. We'll start by instantiating a Server and opening a server session with enough privileges to modify the Module Database.

Each Server has a PIModuleDB property. The modules within the PIModuleDB are stored in the PIModuleDB.PIModules collection. This collection exists on the PI Server in a database similar to the PI Point Database. The PIModules collection is a hierarchical presentation of modules; much like a file system directory structure. We could add our module directly to this collection, but, just like creating all your files in the root directory, this could get very crowded. For our example, we'll create a module to act as a "folder" to contain tic-104, and then we'll create the module for tic-104. Here's the VB code:

Option Explicit

Dim Srv As Server

Dim Controllers As PIModule

Dim Tic104 As PIModule

Private Sub CreateTic104()

Set Srv = PISDK.Servers.DefaultServer

Srv.Open ("uid=piadmin")

Set Controllers = Srv.PIModuleDB.PIModules.Add("Controllers")

Set Tic104 = Controllers.PIModules.Add("tic-104")

End Sub

On our default server we've created, at the root level, a module called "Controllers." Then, in the controller's collection of modules we created the module "tic-104." We've effectively created two hierarchical nodes in the Module Database on the default server:


  1. Getting to the PI Time Series Data

Our goal is to get to the data--we need references to the seven PI Points that make up this controller. Each module has a collection called PIAliases. This is a collection of PIAlias objects. A PIAlias is a reference to a PI Point through a common name or alias. The PI-SDK presents a PI Point through a PIPoint object. Here's the code to do this:

Private Sub CreateTheAliases()

Dim pts(7) As PIPoint

Set Srv = PISDK.Servers.DefaultServer

Srv.Open ("uid=piadmin")

Set Controllers = Srv.PIModuleDB.PIModules.Item("Controllers")

Set Tic104 = Controllers.PIModules.Item("tic-104")

' Create refrences to the PI Points

Set pts(1) = Srv.PIPoints.Item("tic-104.pv")

Set pts(2) = Srv.PIPoints.Item("tic-104.sp")

Set pts(3) = Srv.PIPoints.Item("tic-104.o")

Set pts(4) = Srv.PIPoints.Item("tic-104.i")

Set pts(5) = Srv.PIPoints.Item("tic-104.p")

Set pts(6) = Srv.PIPoints.Item("tic-104.d")

Set pts(7) = Srv.PIPoints.Item("tic-104.m")

 

'Create the aliases in our controller module

Tic104.PIAliases.Add "ProcessVariable", pts(1)

Tic104.PIAliases.Add "SetPoint", pts(2)

Tic104.PIAliases.Add "Output", pts(3)

Tic104.PIAliases.Add "Integral", pts(4)

Tic104.PIAliases.Add "Proportion", pts(5)

Tic104.PIAliases.Add "Derivative", pts(6)

Tic104.PIAliases.Add "Mode", pts(7)

End Sub

At this point we have a module that represents the control loop tic-104. We've added some organization by adding the module to the Controllers module. Pertinent PI Points are associated with the module through the aliases collection. There's usually other data that needs to be associated with controllers, perhaps a manufacturer's model number, a link to a maintenance database, or tuning information. Every PIModule has a PIProperties collection for these purposes. The PIProperties collection is used store any user or application data with a module.

  1. Module Properties

The PIProperties collection is a hierarchical data collection associated with modules. This collection is a place to store data about the module. The PIProperties collection is a collection of PIProperty objects. A PIProperty has a Name, a Value, and its own PIProperties collection. The Value can be nearly any VARIANT--strings, numeric, dates, stored procedures, and even binary objects, such as images. Collections of data can be associated with each PIProperty through the PIProperties collection.

Lets continue with the controller example by adding some data to the controller. We will add some manufacturers information and some tuning information. For the manufacturer, we will add the company name, address, model number, and serial number. For tuning information, we will add a tuning date the technician's name, and some comments. This data could be added to directly to the modules PIProperty collection. But since PIProperties supports hierarchy, we can add this data in an organizationally clear manner by creating two PIProperty entries--one for manufacturer data, the other for tuning data. Then add data to the appropriate property properties collection. Here's the Visual Basic code:

Private Sub CreateTheProperties()

Dim MfgData As PIProperty

Dim InstallationData As PIProperty

Dim Prop As PIProperty

Set Srv = PISDK.Servers.DefaultServer

Srv.Open ("uid=piadmin")

Set Controllers = Srv.PIModuleDB.PIModules.Item("Controllers")

Set Tic104 = Controllers.PIModules.Item("tic-104")

Set MfgData = Tic104.PIProperties.Add("Manufacturer Data")

Set InstallationData = Tic104.PIProperties.Add("Installation Data")

Set Prop = MfgData.PIProperties.Add("Name", "Acme Controller, Inc.")

Set Prop = MfgData.PIProperties.Add("Model", "ABC-4331")

Set Prop = MfgData.PIProperties.Add("Serial Number", "102938475")

Set Prop = InstallationData.PIProperties.Add("Technician", "John Doe")

Set Prop = InstallationData.PIProperties.Add("Comments", "No problem.")

End Sub

  1. Integrating With an Application

We now have a module that represents controller tic-104; we can easily find the associated time series and Meta data. This organization is not of much use unless an application can use it. There are many ways this data can be used; for example, a controller evaluation and maintenance application. Future versions of PI ProcessBook will be PI Module Database aware; but through VBA we can bring the MDB awareness to PI ProcessBook now. We will start with the original ProcessBook display of tic-104 and generalize it to load from a module.

First we'll display the manufacturer and installation data in the PI ProcessBook display. PI ProcessBook is an OLE container, so we'll add two ListView controls to the display, and load the properties. This demo assumes some familiarity with adding controls to PI ProcessBook display and VBA programming. Here's the VBA code populates the two ListViews and the PI ProcessBook Display:

Option Explicit

Dim Srv As Server

Dim Controllers As PIModule

Dim Controller As PIModule

Private Sub Display_Open()

Set Srv = PISDK.Servers.DefaultServer

Srv.Open "uid=piadmin"

Set Controllers = Srv.PIModuleDB.PIModules.Item("controllers")

Set Controller = Controllers.PIModules.Item("tic-104")

LoadMfgData

LoadInstallationData

End Sub

Private Sub LoadMfgData()

Dim MfgData As PIProperty

Dim Prop As PIProperty

Dim strEntry As String

Dim mItem As ListItem

Dim iIndex As Integer

lvwMfgData.ListItems.Clear

lvwMfgData.ColumnHeaders.Clear

lvwMfgData.ColumnHeaders.Add , , "Property", lvwMfgData.Width * 3 / 16

lvwMfgData.ColumnHeaders.Add , , "Value", lvwMfgData.Width * 12 / 16

lvwMfgData.View = lvwReport

Set MfgData = Controller.PIProperties.Item("Manufacturer Data")

iIndex = 1

For Each Prop In MfgData.PIProperties

strEntry = Prop.Name & ": " & Prop.Value

Set mItem = lvwMfgData.ListItems.Add(iIndex, Prop.Name, Prop.Name)

iIndex = iIndex + 1

mItem.SubItems(1) = Prop.Value

Next Prop

End Sub

Private Sub LoadInstallationData()

Dim InstallationData As PIProperty

Dim Prop As PIProperty

Dim strEntry As String

Dim mItem As ListItem

Dim iIndex As Integer

lvwInstallationData.ListItems.Clear

lvwInstallationData.ColumnHeaders.Clear

lvwInstallationData.ColumnHeaders.Add , , "Property", lvwInstallationData.Width * 3 / 16

lvwInstallationData.ColumnHeaders.Add , , "Value", lvwInstallationData.Width * 12 / 16

lvwInstallationData.View = lvwReport

SetInstallationData = Controller.PIProperties.Item("Installation Data")

iIndex = 1

For Each Prop In InstallationData.PIProperties

strEntry = Prop.Name & ": " & Prop.Value

Set mItem = lvwInstallationData.ListItems.Add(iIndex, Prop.Name, Prop.Name)

iIndex = iIndex + 1

mItem.SubItems(1) = Prop.Value

Next Prop

End Sub

The code that loads the ListViews knows nothing about the data. It simply iterates through each property, grabs the name and value, and adds it to the view. If we enhance our module, for example, add a controller type to the Manufacturer Data, the new information is displayed next time the display is loaded; no changes to the client application are required:

This PI ProcessBook display presents a standard view of control loop operation, as well as manufacturer and installation information. Most PID loops are similar--we should be able to re-use this display on any controller. The first step in display re-use is adding VBA code to replace the PI Points we added during display configuration with assignment from the module database. This is a matter of setting the TagName or Trace property of the display objects. PI ProcessBook automatically assigns names to these objects. PI ProcessBook build mode allows a right mouse click to bring up each object's properties. The object name is displayed; you can also rename the object. The following VBA functions retrieves the appropriate aliases from the controller module, then uses the alias data source property to assign the PI Point:

Private Sub LoadAliasesIntoTrend()

Dim Alias As PIAlias

Dim Pt As PIPoint

' Get rid of existing points

While Trend.TraceCount <> 0

Trend.RemoveTrace 1

Wend

Set Alias = Controller.PIAliases.Item("Output")

Set Pt = Alias.DataSource

Trend.AddTrace Pt.Name

Set Alias = Controller.PIAliases.Item("ProcessVariable")

Set Pt = Alias.DataSource

Trend.AddTrace Pt.Name

Set Alias = Controller.PIAliases.Item("Setpoint")

Set Pt = Alias.DataSource

Trend.AddTrace Pt.Name

Trend.GetFormat().ShowTagName = True

End Sub

Private Sub LoadAliasesIntoBarChart()

Dim Alias As PIAlias

Dim Pt As PIPoint

Set Alias = Controller.PIAliases.Item("ProcessVariable")

Set Pt = Alias.DataSource

Bar.SetTagName (Pt.Name)

Set Alias = Controller.PIAliases.Item("SetPoint")

Set Pt = Alias.DataSource

Bar1.SetTagName (Pt.Name)

Set Alias = Controller.PIAliases.Item("Output")

Set Pt = Alias.DataSource

Bar4.SetTagName (Pt.Name)

End Sub

Private Sub LoadAliasesIntoValues()

Dim Alias As PIAlias

Dim Pt As PIPoint

Set Alias = Controller.PIAliases.Item("Proportion")

Set Pt = Alias.DataSource

Value.SetTagName (Pt.Name)

Set Alias = Controller.PIAliases.Item("Integral")

Set Pt = Alias.DataSource

Value2.SetTagName (Pt.Name)

Set Alias = Controller.PIAliases.Item("Derivative")

Set Pt = Alias.DataSource

Value3.SetTagName (Pt.Name)

Set Alias = Controller.PIAliases.Item("Mode")

Set Pt = Alias.DataSource

Value9.SetTagName (Pt.Name)

End Sub

We need more controllers. Processing enterprises should have plenty. We'll resort to more simulator points and create 9 more controller modules. The programmatic controller creation steps are identical to those covered; that code can be modified to add more, or the configuration tool can be used. Once the controllers are created, we'll add a pick list control to the PI ProcessBook display. On display load, we'll add references to all the controllers.

Here are the steps taken to get to the following display:

  1. Replace the text label "tic-104" with a combo box control. The combo box will be used to select the controller to view.
  2. Add the following VBA function to load the controllers in the combo box:

Private Sub LoadComboBox()

Dim ctlr As PIModule

For Each ctlr In Controllers.PIModules

cboControllers.AddItem ctlr.Name

Next ctlr

cboControllers.ListIndex = 0 ' set first controller as active

End Sub

  1. Add a combo box click event handler. The handler gets the selected controller, then replaces the display PI Points:

Private Sub cboControllers_Click()

If cboControllers.Text <> Controller.Name Then

Set Controller = Controllers.PIModules.Item(cboControllers.Text)

LoadMfgData

LoadInstallationData

LoadAliasesIntoDisplay ' this calls the three load functions

End If

End Sub

Here is the display after loading:

The display allows selecting a different controller, and displays the appropriate PI Points:

  1. Scalability and Hierarchy

The example uses the combo box to display 10 controllers. An average processing site is likely to have over 100 controllers. A combo box starts getting over whelmed with 20 or so entries. To make this point, we can add a 200 hundred controllers. The problems become obvious. The application loads more slowly, because we need to add 210 entries to the combo box. With 210 entries in the combo box it is much harder to find the controller in question.

Clearly a more scalable approach is required. The answer is to organize modules with more hierarchy. Working from the controllers, up, we will build the hierarchy with the following approach.

Note: This approach is an ISA S88 batch-processing example. There are no rules on what the hierarchy should be and no limits on the number of hierarchical strategies or schemas.

  1. Group controllers with the unit they control.
  2. Group units by production lines.
  3. Group production lines by production area.
  4. Group production areas by site.
  5. Group sites by enterprise. The enterprise will be the top of the hierarchy.
  6. PIHeadingSets

This approach uses 6 categories--the above 5, plus a category for the controllers. The PI Module Database provides a mechanism to name the categories and set a recommended hierarchical level. Each category can be assigned a PIHeading. The PIHeadings are grouped in a PIHeadingSet. Since there are no limits to hierarchical schemas there are no limits on the number of PIHeadingSets that may be configured. We will implement the groupings by creating a heading set and adding 6 headings. The headings are used to identify or label modules in this schema. Here's a table representation of the heading set:

 

Here is some VB code, which configures the heading set for this schema:

Private Sub cmdCreateHeadingSet_Click()

Dim Srv As Server

Dim HeadingSet As PIHeadingSet

Dim Heading As PIHeading

Set Srv = Servers.DefaultServer

Srv.Open

Set HeadingSet = Srv.PIModuleDB.PIHeadingSets.Add("S-88 Equipment", "S 88 equipment for example")

Set Heading = HeadingSet.PIHeadings.Add("Enterprise", "S-88 Equipment definition", 1)

Set Heading = HeadingSet.PIHeadings.Add("Site", "S-88 Equipment definition", 2)

Set Heading = HeadingSet.PIHeadings.Add("Area", "S-88 Equipment definition", 3)

Set Heading = HeadingSet.PIHeadings.Add("Line", "S-88 Equipment definition", 4)

Set Heading = HeadingSet.PIHeadings.Add("Unit", "S-88 Equipment definition", 5)

Set Heading = HeadingSet.PIHeadings.Add("Controller", "S-88 Equipment definition", 6)

End Sub

The PIHeading object has three properties: name, description, and level. The name must be unique within the heading set. The level suggests a hierarchical position, where the lower number is hierarchically superior. The level property does not enforce hierarchy.

  1. Hierarchical Equipment

We build the hierarchical equipment layout by starting at top and working our way down to the units. The controllers will be part of a unit. Since the same module can exist in multiple hierarchical positions there is no need to re-create each controller, or move the controllers. We will create the new hierarchy, and then insert the controllers into the appropriate unit. The PIModules method accomplishes this task. Here's the code to create the hierarchy, and insert the controllers.

Here's a block of VB coding where we create the higher level modules then insert some controllers in a unit. The insert method allows referencing an existing controller in the PIModules collection of another module.

Dim Enterprise As PIModule

Dim Site As PIModule

Dim Area As PIModule

Dim Line As PIModule

Dim Unit As PIModule

Dim Controllers As PIModule

Dim Controller As PIModule

Set Srv = PISDK.Servers.DefaultServer

Srv.Open ("uid=piadmin")

On Error Resume Next

Set Controllers = Srv.PIModuleDB.PIModules.Item("Controllers")

Set Enterprise = Srv.PIModuleDB.PIModules.Add("Demo Enterprises")

Set Site = Enterprise.PIModules.Add("Cleveland")

Set Area = Site.PIModules.Add("Area C-1")

Set Line = Area.PIModules.Add("Line 4")

Set Unit = Line.PIModules.Add("R-401")

Set Controller = Controllers.PIModules.Item("tic-104")

Unit.PIModules.Insert Controller

Set Controller = Controllers.PIModules.Item("tic-105")

Unit.PIModules.Insert Controller

Here's a view of this hierarchy from the PI Module Database editor:

  1. PI ProcessBook Application

Now that we have a hierarchical presentation of the equipment we need to program the PI ProcessBook display to take advantage of it. We'll take two approaches: Cascading set of combo boxes and a Tree View. The cascading combo box approach will use a combo box for site, area, line, unit, and finally the controllers. Changing a selection in the upper box will force updating the lower combo boxes with the appropriate modules.

Here's the VBA code that loads the combo boxes; this is called on display open:

Private Sub LoadAllComboBoxes()

Dim i As Integer

For Each Site In Enterprise.PIModules

cboSites.AddItem Site.Name

Next Site

Set Site = Enterprise.PIModules.Item(1)

For Each Area In Site.PIModules

cboAreas.AddItem Area.Name

Next Area

Set Area = Site.PIModules.Item(1)

For Each Line In Area.PIModules

cboLines.AddItem Line.Name

Next Line

Set Line = Area.PIModules.Item(1)

For Each Unit In Line.PIModules

cboUnits.AddItem Unit.Name

Next Unit

Set Unit = Line.PIModules.Item(1)

For Each Controller In Unit.PIModules

cboControllers.AddItem Controller.Name

Next Controller

Set Controller = Unit.PIModules.Item(1)

cboSites.ListIndex = 0

cboAreas.ListIndex = 0

cboLines.ListIndex = 0

cboUnits.ListIndex = 0

cboControllers.ListIndex = 0

End Sub

There is a selection handler for each combo box. The higher-level combo boxes "select" the first item in the next lower box. This forces a cascade of selection events, forcing all the boxes, then the display to update. Here's the code:

Private Sub cboControllers_Click()

Dim Reload As Boolean

Reload = False

If Controller Is Nothing Then

Reload = True

ElseIf cboControllers.Text <> Controller.Name Then

Reload = True

End If

If Reload Then

'Set Controller = Controllers.PIModules.Item(cboControllers.Text)

Set Controller = Unit.PIModules.Item(cboControllers.Text)

LoadMfgData

LoadInstallationData

LoadAliasesIntoDisplay

End If

End Sub

Private Sub cboUnits_Click()

Dim Reload As Boolean

Reload = False

If Unit Is Nothing Then

Reload = True

ElseIf cboUnits.Text <> Unit.Name Then

Reload = True

End If

If Reload Then

Set Unit = Line.PIModules.Item(cboUnits.Text)

cboControllers.Clear

For Each Controller In Unit.PIModules

cboControllers.AddItem Controller.Name

Next Controller

If cboControllers.ListCount > 0 Then

cboControllers.ListIndex = 0

End If

End If

End Sub

Private Sub cboLines_Click()

Dim Reload As Boolean

Reload = False

If Line Is Nothing Then

Reload = True

ElseIf cboLines.Text <> Line.Name Then

Reload = True

End If

If Reload Then

Set Line = Area.PIModules.Item(cboLines.Text)

cboUnits.Clear

For Each Unit In Line.PIModules

cboUnits.AddItem Unit.Name

Next Unit

If cboUnits.ListCount > 0 Then

cboUnits.ListIndex = 0

Else

cboControllers.Clear

End If

End If

End Sub

Private Sub cboAreas_Click()

Dim Reload As Boolean

Reload = False

If Area Is Nothing Then

Reload = True

ElseIf Area.Name <> cboAreas.Text Then

Reload = True

End If

If Reload Then

Set Area = Site.PIModules.Item(cboAreas.Text)

cboLines.Clear

For Each Line In Area.PIModules

cboLines.AddItem Line.Name

Next Line

If cboLines.ListCount > 0 Then

cboLines.ListIndex = 0

Else

cboUnits.Clear

cboControllers.Clear

End If

End If

End Sub

Private Sub cboSites_Click()

If cboSites.Text <> Site.Name Then

Set Site = Enterprise.PIModules.Item(cboSites.Text)

cboAreas.Clear

For Each Area In Site.PIModules

cboAreas.AddItem Area.Name

Next Area

If cboAreas.ListCount > 0 Then

cboAreas.ListIndex = 0

Else

cboLines.Clear

cboUnits.Clear

cboControllers.Clear

End If

End If

End Sub

Here's the PI ProcessBook display. Notice the combo boxes for Site, Area, Line, Unit, and Controller. The Display is obviously faster, and finding a controller is much easier.

Another navigation option is to use a tree control. This approach populates a tree control with the module structure of our enterprise. On node selection, the event handler looks at the module heading. If it is a controller, the display is updated.

The first step is, on display load, to load the modules into the tree control. This example, loads the entire structure. An improvement would be to load the lower tree view nodes as they are selected. Hierarchical data structures can be loaded using re-entrant code--that is, functions that call themselves. Here's the code that loads the modules into the tree view:

Private Sub LoadTreeView()

Dim EnterpriseNode As Node

Dim i As Long

tvwEnterprise.nodes.Clear

tvwEnterprise.Sorted = False

Set EnterpriseNode = tvwEnterprise.nodes.Add(, , "Enterprise", "Enterprise")

LoadTreeViewNodes Enterprise.PIModules, EnterpriseNode, i

EnterpriseNode.Expanded = True

End Sub

Private Sub LoadTreeViewNodes(Modules As PIModules, ParentNode As Node, i As Long)

Dim Module As PIModule

Dim SubNode As Node

Dim strKey As String

' "Key" property of a node must be unique. Since modules can live in multiple positions, we can't rely on

' the module's unique id. So we'll append an integer to the unique ID

For Each Module In Modules

i = i + 1

strKey = Module.UniqueID & "-" & i

Set SubNode = tvwEnterprise.nodes.Add(ParentNode, tvwChild, strKey, Module.Name)

LoadTreeViewNodes Module.PIModules, SubNode, i ' re-entrancy is the best way to deal with hierarchies

Next Module

End Sub

Here's the display. For demonstration purposes, the cascading combo boxes were not removed. This display supports the combo box and tree view approach.

The node selection code does most of the work. This code must find the module associated with the node; then update the display if it is a controller.

The node text is also the module name. The technique used is to walk up the node parents, building an array of nodes, then walk down the array, using the node.text property to get the module item. Alternatively you could store every module with the tree view. However, this will not scale very well. Here's the code:

Private Sub tvwEnterprise_NodeClick(ByVal Node As MSComctlLib.Node)

Dim Module As PIModule

Dim i As Long

Dim ParentNodes(20) As Node

i = 1

Set ParentNodes(i) = Node.Parent

On Error GoTo Done

While True

i = i + 1

' following line will cause an error after the last parent and go to the "Done" label

Set ParentNodes(i) = ParentNodes(i - 1).Parent

Wend

Done:

i = i - 3 ' step below enterprise

Set Module = Enterprise

While i >= 1

' Use the array of nodes to walk down the hierarchy

Set Module = Module.PIModules.Item(ParentNodes(i).Text)

i = i - 1

Wend

Set Module = Module.PIModules.Item(Node.Text)

If Module.PIHeading.Name = "Controller" Then

Set Controller = Module

LoadMfgData

LoadInstallationData

LoadAliasesIntoDisplay

End If

End Sub

Enabling Operational Intelligence