Calculation Formulas

About Calculation Formulas

To perform a calculation using the Calculation collector, you must define the calculation formula. You can do so in one of the following ways:Before you create calculation formulas, refer to the general guidelines.

There are two predefined global values called Result and Quality. These global values control the value and quality of the output sample. If the Result is not set in the formula, then no sample is stored.

General Guidelines for Defining a Calculation Formula

This section provides guidelines that you must follow when defining a calculation formula.

Identify Time Intensive Calculations

Use the Calculation Execution Time property of each tag to identify time-intensive queries. In Historian Administrator, look for the Execution Time on the Calculation section for an estimate of how long, on average, it takes for the calculation per tag (starting from the time the collector was started).

You can also include that column when you export tags to Excel using the Excel Add-In feature. For information, refer to Exporting Tags.

You can also include that column (AverageCollectionTime) when you query the ihTags table using the Historian OLE DB Provider. Sorting by this column will let you find them fast.

Troubleshoot Issues with Large Configurations

If the timestamps of your raw samples appear slightly old, do not assume that the collector has stopped working. It is possible that the collector is just running behind.

For instance, if you have a report rate of 15,000, but the newest raw sample that you see is 20-30 minutes old, wait for 1-2 minutes, and review the newest raw sample again. If the collector stopped, the newest raw sample will be unchanged. If it did change, then the engine is still running, but is lagging behind. If that happens, check if the collector overrun count is increasing. If yes, the collector is dropping samples, and you must decrease the load.

Error Handling in VBScript

Start each script with the On Error Resume Next statement so that errors are trapped. If you use this statement, the script runs even if a run-time error occurs. You can then implement error handling in your VBScript.

It is a good practice to include statements in your VBScript that catch errors when you run the script. If there is an unhandled error, a value of 0 with a bad data quality is stored. When you catch an error in the VBScript, consider including a statement in your calculation that sets the Quality=0 when the error occurs. (The 0 value means that the quality is bad.) If you do not specifically include this setting in your script, Historian stores a good data quality point (Quality=100), even if an error has occurred in your formula. If Quality=100 is not appropriate for your application, consider setting the quality to 0.

You cannot use the On Error GoTo Label statement for error handling, as it is not supported in VBScript. As a workaround, you can write code in the full Visual Basic language and then place it in a .DLL so that you can call it from within your VBScript using the CreateObject function. For examples of calculations that use the CreateObject function, refer to Examples of Calculation Formulas.

Unsupported VBScript Functions

You can use any VBScript syntax to build statements in a calculation formula with the exception of the following functions:
  • MsgBox
  • InputBox

Milliseconds not Supported in VBScript

The CDate() function does not support the conversion of a time string with milliseconds in it. Whenever you use the CDate() function, a literal time string, or a time string with a shortcut, do not specify milliseconds in the time criteria. Milliseconds are not supported in VBScript.

You cannot use milliseconds in times passed into built-in functions such as the PreviousTime and NextValue functions. For example, you cannot loop through raw samples with millisecond precision.

Notes on VBScript Time Functions

Using the VBScript time functions such as Now, Date, or Time can lead to unexpected results, especially in recalculation or recovery scenarios. To avoid these issues, use the CurrentTime built-in function provided by Historian, instead of Now, Date, or Time. For example, the VBScript Now is always the clock time of the computer and is likely not useful when recalculating or recovering data for times in the past. However, the "Now" time shortcut is equivalent to CurrentTime and can be used as input to the other built in functions.

Using Quotation Marks in VBScript

If you want to use quotation marks in a tag name, you must insert a double quotes for each quotation mark that you want to use, as required for proper VBScript syntax. For example, if you want to get the current value of a tag named TagCost"s, you must enter:
Result = CurrentValue("TagCost""s")

In this example, note the double quotation marks that appear before the letter s in the TagCost"s name in the formula.

Avoiding Circular References in VBScript

Do not use circular references in calculation formulas. For instance, if the tag name is Calc1, a formula with a circular reference would be Result=CurrentValue("Calc1"). Whether the tag is polled or unsolicited, you get a bad value back using the circular reference.

Uninterrupted Object Method Calls

Object method calls are not interrupted. It is possible to exceed the Calculation Timeout setting if you have a method call that takes a long time to execute. The Calculation Timeout error still occurs, but only after the method completes.

Help for VBScript

You can get detailed Help for VBScript by referencing the Microsoft documentation on the MSDN web site. A VBScript User's Guide and Language Reference is available here: http://msdn.microsoft.com/en-us/library/t0aew7h6.aspx

Avoiding Deleted Tags

You can reference a deleted tag in a calculation formula, without an error appearing. For instance, you could enter a formula such as Result=CurrentValue("DeletedTag"), where DeletedTag is the name of the deleted tag. You can do this because when you delete a tag, Historian removes deleted tags from the Tag Database (so you cannot browse for it), but it retains the data for that tag in the archive.

However, it is recommended that you do not reference deleted tag names in your calculation formulas, because if the archive files are removed with the data for the deleted tag, the calculation will not work properly.

Create a Calculation Formula Using a VBScript Code

About this task

This topic describes how to create a calculation formula by entering a VBScript code. You can also create a calculation formula using the Insert Function wizard.
Important: If a tag contains bad data quality, you cannot store its value through a calculation formula. For example, if your VBScript includes: Result = 7 Quality = 0, Historian does not store the 7, it stores 0.

Before you begin

Create the tag that you want to use to store the calculation results. You can create the tag manually using Historian Administrator or the Web Admin console. Or, you can copy a tag.

Procedure

  1. Access Historian Administrator.
  2. Select Tags, select the tag for which you want to create a calculation formula, and then select Calculation.
  3. If you want to perform an unsolicited (also called event-based) calculation, add the trigger tags to the calculation:
    1. In the Calculation Triggers section, select Add.
      The Insert Function Wizard window appears.
    2. Under Select Function, in the Type field, select Add A Calculation Trigger.
    3. Under Tag Browse Criteria, enter the search criteria to find the tag.
      The search results appear in the Browse Results section.
    4. Select the tag that you want to add, and then select Insert.
  4. In the Calculation field, enter the calculation formula using the VBScript syntax.
    Tip:

Create a Calculation Formula Using the Wizard

About this task

This topic describes how to create a calculation formula using the Insert Function wizard. You can also create a calculation formula using a VBScript code.

Before you begin

  1. Create the tag that you want to use to store the calculation results. You can create the tag manually using Historian Administrator or the Web Admin console. Or, you can copy a tag.
  2. Access Historian Administrator, select Collectors > Advanced, and then disable the On-line Tag Configuration Changes option. If you do so, each time you update a calculation formula, the collector does not reload tags.

Procedure

  1. In Historian Administrator, select Tags, select the tag for which you want to create a calculation formula, and then select Calculation.
  2. In the Calculation section, remove Null (retain Result =).
    Tip: Avoid selecting other tags until you save your changes or you will lose your code changes.
  3. Select Wizard.
    The Insert Function Wizard window appears.
  4. Under Select Function, select values in the available fields, and then select Insert.
    For information on a list of the available types and associated functions, refer to Types of Functions Supported by the Wizard. For information on each pre-defined function, refer to Built-In Functions. In addition to the built-in functions, you can create your own customized functions.
  5. If you want to perform an unsolicited (also called event-based) calculation, add the trigger tags to the calculation:
    1. In the Calculation Triggers section, select Add.
      The Insert Function Wizard window appears.
    2. Under Select Function, in the Type field, select Add A Calculation Trigger.
    3. Under Tag Browse Criteria, enter the search criteria to find the tag.
      The search results appear in the Browse Results section.
    4. Select the tag that you want to add, and then select Insert.

Create a User-Defined Function

About this task

This topic describes how to create your own function to use in a calculation formula. You can also use any of the built-in functions.

Before you begin

Create the tag that you want to use to store the calculation results. You can create the tag manually using Historian Administrator or the Web Admin console. Or, you can copy a tag.

Procedure

  1. In Historian Administrator, select Tags, select the tag for which you want to create a calculation formula, and then select Calculation.
  2. In the Calculation section, remove Null (retain Result =).
    Tip: Avoid selecting other tags until you save your changes or you will lose your code changes.
  3. Select Functions.
    The User Defined Functions window appears.
  4. Select New.
    The Edit Function window appears.
  5. Define the function.
    You can build formulas using the wizard, or create it manually by entering functions in the Edit Function box. For information, refer to User-Defined Functions.
  6. Select Syntax to check for errors.
  7. Select Update.
    Your function appears in the list, and is available for use in other calculations as well.
  8. To use the function, select Insert Function.
    The function is inserted in your calculation formula.

Built-in Functions

This topic describes the built-in functions that you can use to create a calculation formula. You can also create your own calculation function.
Note:
  • In this table, Time refers to the actual time; this time can include absolute and relative time shortcuts. See the Date/Time Shortcuts and Relative Date/Time Shortcuts sections for more information.
  • You cannot control the timestamp of the stored sample. It is determined by the triggering tag or polling schedule.
  • You cannot use microseconds for any of the built-in calculation functions.

    For all the functions that retrieve previous values, it is similar to performing a RawByNumber query with a count of 1 and direction of backward. A less-than operation (not less-than-or-equal-to) is used on the timestamp to get the sample. Similarly, for all the functions that retrieve next values, it is similar to performing a RawByNumber query with a count of 1 and direction of forward. A greater-than operation (not greater-than-or-equal-to) is used on the timestamp to get the sample.

Function Name Description
CurrentValue(<tag name>) The value of the tag, interpolated to the calculation execution time. The CurrentValue function returns 0 if the quality is 0 (bad quality). This occurs if you initialized it to 0, or if a previous call failed.
CurrentQuality(<tag name>) The current quality of the tag (0 for bad quality and 100 for good quality).
CurrentTime

The calculation execution time, which becomes the timestamp of the stored value.

For real time processing of polled tags, the calculation execution time is the time when the calculation is triggered. For unsolicited tags, the calculation execution time is the timestamp delivered with the subscription.

Note: When a calculation is performed, the timestamp of the result is the time that the calculation has begun, not the time that it completed.

For recovery of polled or unsolicited tags, the calculation execution time is the time when the calculation would have been performed if the collector were running.

PreviousValue(<tag name>, Time) The tag value of the raw sample prior to the current time.
PreviousQuality(<tag name>, Time) The quality of the tag (0 for bad quality and 100 for good quality) prior to the current time.
PreviousGoodValue(<tag name>, Time) The latest good value of the raw sample prior to the current time.
PreviousGoodQuality(<tag name>, Time) The good quality of the raw sample prior to the current time.
PreviousTime(<tag name>, Time) The timestamp of the raw sample prior to the current time.
PreviousGoodTime(<tag name>, Time) The timestamp of the latest good quality of the raw sample prior to the current time.
NextValue(<tag name>, Time) The value of the raw sample after the current timestamp.
NextQuality(<tag name>, Time) The quality of the tag (0 for bad quality and 100 for good quality) after the current time.
NextTime(<tag name>, Time) The timestamp of the raw sample after the current timestamp.
NextGoodValue(<tag name>, Time) The value of the good raw sample after the current time.
NextGoodQuality(<tag name>, Time) The good quality of the raw sample after the current time.
NextGoodTime(<tag name>, Time) The timestamp of the good raw sample after the current time.
InterpolatedValue(<tag name>, Time) The tag value, interpolated to the time that you enter.
Calculation Unfiltered calculated data query that returns a single value, similar to the Excel Add-In feature. For a list of the calculation mode, refer to Calculation Modes.
AdvancedCalculation Unfiltered calculated data query that returns a single value, similar to the Excel Add-In feature. For a list of the calculation mode, refer to Calculation Modes.
AdvancedFilteredCalculation Advanced Filtered calculated data query that returns a single value, similar to the Excel Add-In feature.
FilteredCalculation Filtered calculated data query that returns a single value, similar to the Excel Add-In feature.
LogMessage(string_message) Allows you to write messages to the Calculation collector or the Server-to-Server collector log file for debugging purposes. The collector log files are located in the Historian\LogFiles folder.
Note: The LogMessage function is the only function that does not appear in the wizard.
GetMultiFieldValue(Variable, <field name>) Returns the value of the field that you have specified. The variable contains the current value of all the fields of a multi-field tag. Before using this function, you must read the tag into a variable, using the CurrentValue() function. You can then use the GetMultiFieldValue function to access the value of the field.

The value of the field that you enter must be the same as the name of the field in the user defined type. If the field name is not found, a null value is returned.

GetMultiFieldQuality(Variable, <field name>) Returns the quality (0 for bad quality and 100 for good quality) of the field that you have specified. The variable contains the current value of all the fields of a multi-field tag. Before using this function, you must read the tag into a variable, using the CurrentValue() function. You can then use the GetMultiFieldValue function to access the value of the field.

The value of the field that you enter must be the same as the name of the field in the user-defined type. If the field name is not found, a null value is returned.

If the user-defined type can store individual quality, you get the field quality. Otherwise, you get the sample quality.

SetMultiFieldValue(Variable, <field name>, Value, Quality) Sets the value and the quality for the field that you have specified.

You can use this function to construct a multifield value containing values for each field, and then use the result= syntax to store the value in Historian.

Counting the Number of Bad Quality Samples

The following example shows how to loop through samples of a tag named C2 to count the number of bad quality samples.
Dim count, starttime, endtime, tagquality count=0
StartTime=CurrentTime EndTime=DateAdd("n",-1,StartTime) Do while StartTime>EndTime
TagQuality=PreviousQuality("C2",StartTime)
startTime=PreviousTime("C2",StartTime) IF TagQuality=0 THEN
count=count + 1
END IF loop Result=count

Counting the Number of Collected Digital 1s For a Tag

The following example counts the number of collected digital 1s for a tag so that, for instance, you can determine how many times a pump is turned ON and OFF.

Dim count, starttime, endtime,tagquality,TagValue 
count=0
StartTime=CurrentTime 
EndTime=DateAdd("h",-1,StartTime) 
On error resume next
Do while StartTime>=EndTime 
TagValue=PreviousValue("FIX.DI.F_CV",StartTime) 
TagQuality=PreviousQuality("FIX.DI.F_CV",StartTime) 
startTime=PreviousTime("FIX.DI.F_CV",StartTime)
IF TagQuality=100 AND TagValue=1 then 
count=count + 1
END IF
loop
Result=count

Determining the Trigger When Using Multiple Trigger Tags

The following example shows how to determine which tag triggered the calculation, from a list of two possible trigger tags. The example compares the two trigger tags and determines which one has the newest raw sample. This method of getting the newest raw sample can also be used to determine if a remote collector is sending data or is disconnected from the server.

In this example, archive compression is disabled for both of these tags.

dim timetag1 
dim timetag2 
dim tag1
dim tag2

tag1 = "BRAHMS.AI1.F_CV" 
tag2 = "BRAHMS.AI2.F_CV"

' Get the timestamp of the newest raw sample for tag1:
timetag1 = previousTime(tag1, CurrentTime)

' Get the timestamp of the newest raw sample for tag2:
timetag2 = previousTime(tag2, CurrentTime)

if timetag1 > timetag2 then
' If tag1 triggered me, then:
result = 1 else
' If tag2 triggered me, then:
result = 2 
end if

Using Array or Multifield Data in Calculation

You can create tags of arrays and multifield types and use the Calculation collector, Server-to-Server collector, Server-to-Server distributor with these tags.

Arrays
To use the Array data as input to a calculation formula you can use the name of the array tag like "Array1" or the individual element of the array like "Array1[4]". For example, if you have an array tag "Array1" of floating point values and a calculation tag "FloatCalc1" of float data type, then you can use the array as input to calculate a float value.
result = currentvalue("Array1[4]")+5

You can use Calculation() function to read the array tag as shown in the following code.

Result = Calculation("Array1","Average","Now 1Minute","Now",Quality)

In this example, the calculation tag should be an array tag because the average of an array is an array, not a single value. Each element is averaged over the time range. Since an average of an integer or float array is a floating point value, the calculation tag must be a single or double float array.

If you want to find the minimum of array elements in a given time, then use vbscript code to compute and store the result in a Float tag as shown.

if CurrentValue("Array1[0]") < CurrentValue("Array1[1]") then
    Result = CurrentValue("Array1[0]")
else
    Result = CurrentValue("Array1[1]")
end if
Multifield
If you have a user-defined type "MySample" with fields "r;FloatVal" and "r;IntVal" you can create Tag1 and use the value of one field in an Integer Calc Tag. The destination tag is not a multifield tag.
result = currentvalue("Tag1.IntVal")+5

Storing Array or Multifield data in Calculation tags

Array
If your calculation tag is an array tag, then you can copy the entire array values into it. For example, you can copy the entire values from Array1 into Array2 using the given code.
result = CurrentValue("Array1")
You can take an array value collected from a field device and adjust the values before storing it in another array tag Array2 using this code:
dim x
x=CurrentValue("Array1")
x(1) = x(1)+10
result = x
You can simply construct an array value inside your formula and store it in Array2, for example:
dim MyArray(2)' The 2 is the max index not the size
MyArray(0)=1
MyArray(1)=2
MyArray(2)=3 result = MyArray
Multifield
You can have the collector combine collected data into a multifield tag. Create a calculation Tag1 using the user-defined Type "MySample," then use this formula to fill in the fields:
Dim InputValue, myval,x,y

' get the current value of another multifield tag
InputValue = CurrentValue("tag1")

' get the values of each of the fields
x = GetMultiFieldValue(InputValue, "IntVal")
y = GetMultiFieldValue(InputValue, "floatval")

' store the field values in this tag
SetMultiFieldValue myval,"IntVal",x,100
SetMultiFieldValue myval,"floatval",y,100
Result = myval

Using Array or Multifield data to trigger calculation

Array
You can use the array tag as a trigger tag for your float or array calculation tags. For example, you can use Array1 as a trigger so that when it changes, the "CalcArray1" tag will be updated. You cannot use an individual array element such as "Array1[3]" as a trigger, you must use the entire array tag as the trigger tag.
Multifield
You can use a multifield tag as a trigger tag by either using the tagname "Tag1" or tagname with the field name "Tag1.FloatVal".

Sending Array or Multifield data to a Remote Historian

Array
You can use the Server to Server Collector or Server to Server Distributor to send array data to a destination Historian. If the destination Historian is version 6.0 or later, you can simply browse the tags and add them.

You cannot send an array to the older versions of archiver (Pre 6.0 versions) as these archivers will store the array tags as a blob data type in the destination and you will not be able to read them. However, you can send individual elements of an array to these archivers, for example, result = currentvalue("Array1 [4]").

Multifield
The destination needs to be Historian 6.0 or above to store a multifield tag but you can send individual fields to a pre Historian 6.0 archiver.

For multifield tags, you must create the User Defined Type manually at the destination

You can write an entire multified tag data sample in one write or you can create multiple tags in the destination, one for each field you want to copy. For example, if you have one tag "Tag1" with two fields "FloatVal" and "IntVal" on a source archiver, then you can create two tags ("Tag1.FloatVal" and "Tag1.IntVal") on the destination.

Note: If you change a field name or add or remove fields you must update your collection and your destination tags.

Reading and writing a Multifield tag using MultiField functions

The following example shows how to read an entire multifield tag, using the GetMultiFieldValue function and to write the value to a field in another tag using the SetMultiFieldValue function.

Dim CurrMultifieldValue

' Read the value of a multi field tag into a variable
CurrMultifieldValue = CurrentValue("MyMultifieldTag")

' Read the field value of multifield tag into the temporary variable
F1 = GetMultiFieldValue(CurrMultifieldValue, "Temperature Field")

' Perform a calculation on the value
Celcius = (F1 32)/ 9* 5

' Set the calculated value to another field of the multifield tag 
SetMultiFieldValue(CurrMultifieldValue, "Temperature Field Celcius", Celcius, 100) 
result = CurrMultifieldValue

Types of Functions Supported by the Wizard

The following table describes the types of actions supported by the Insert Function wizard. All the value functions return a single value.

Type of ActionAvailable Functions for the Action
Insert a value
  • Current value
  • Previous value
  • Next value
  • Interpolated value
Insert a calculation
  • Unfiltered calculation
  • Filtered calculation
Insert a timestamp
  • Time shortcut
  • Previous value timestamp
  • Next value timestamp
  • Current time
Check data quality
  • Current value quality
  • Previous value quality
  • Next value quality
Set data quality
  • Set Quality Good
  • Set Quality Bad
Add data valueNone
Insert a tag nameTagname
Insert an alarm calculation
  • Previous Alarm
  • Next Alarm
  • Get Alarm Property
  • Set Alarm Property
  • Add Event
  • New Alarm
  • Update Alarm
  • Return to Normal
Insert a multifield operation
  • GetMultiFieldValue
  • GetMultiFieldQuality
  • SetMultiFIeldValue

User-Defined Functions

In addition to the built-in functions, you can create custom calculation functions. After you create a custom calculation function, it is available for use with other calculations as well.

Functions are useful as shortcuts for large blocks of source code. By creating a function out of commonly used calculation formulas, you can save time and effort instead of typing a few lines of calculation formula every time you want to perform the same operation, it is compressed to a single line.

The syntax of a function is simple:
Function functionname (variable list) 
   [calculation formulas]
End Function

The operations a function performs are contained within the Function / End Function statements. If you need to send data to the function a tag name, for example you simply create a variable in the function's parameters to receive the data. Multiple variables must be separated by commas. These variables exist only within the function.

The following is an example of a function. This function, named checkValue(), looks at a tag and assigns it an alarm if it is over a specified value.

A Function to Assign an Alarm to a Tag Based on a Condition

The following function, named checkValue, assigns an alarm to a tag if the tag value reaches a specified value.
Function checkValue (tagname,sourcename,value) 
  If CurrentValue(tagname) > value Then
  Set AlarmObj = new Alarm 
  AlarmObj.SubConditionName = "HI" 
  AlarmObj.Severity = 750
  AlarmObj.NewAlarm
  "alarmname", "Simulated", "tagname", "Now" 
  checkValue = true
Else 
  checkValue = false
  End If
End Function
If you want to use this function, enter the values for tag name, source name, and value, as shown in the following example:
alm_set = checkValue("DD098.FluidBalance","FluidBalance_ALM",5000)
In this example, if the value of the DD098.FluidBalance tag exceeds 5000, the function returns a true value, indicating that the alarm was set; the alm_set variable will be set to true. Otherwise, the alm_set variable will be set to false.

Date/Time Shortcuts

The following table outlines the date/time shortcuts that you can use in calculation formulas.

Table 1. Date/Time Shortcuts
ShortcutDescription
NowNow (the time and date that you execute the query)
TodayToday at midnight
YesterdayYesterday at midnight
BOYFirst day of year at midnight
EOYLast day of year at midnight
BOMFirst day of month at midnight
EOMLast day of month at midnight

Relative Date/Time Shortcuts

Optionally, you can add or subtract relative times to the following absolute times. You must use them in conjunction with the date/time shortcuts listed in the preceding table (for example, Today+5h+3min instead of 5h3min).
  • Second
  • Minute
  • Hour
  • Day
  • Week

Converting a Collected Value

The following code sample converts a temperature value from degrees Celsius to degrees Fahrenheit.
Result=CurrentValue("Temp F")*(9/5)+32

Calculations Inside Formulas

The following code sample contains a calculation within a formula. In this case, we are taking the average of values of the tag Simulation00001 over the previous hour. Typically, use a polled trigger to schedule the execution of the formula.
Result=Calculation("Simulation00001","Average","Now-1hour","Now",Quality)

Conditional Calculation

The following code sample stores the value of a tag only if it is 100.
IF CurrentQuality("Simulation00001")=100 THEN 
Result=CurrentValue("Simulation00001")
END IF

Combining Tag Values and Assigning a Trigger

The following code sample adds current values of multiple tags using two calculation triggers.
Result=CurrentValue("SERVER1.Simulation00003")+CurrentValue("SERVER1.Simulation00006")
The calculation triggers used in the sample are SERVER1.Simulation0003 and SERVER1.Simulation0006. The calculation is triggered if the value of either Server1.Simulation0003 or Server1.Simulation0006 changes.

Using CreateObject in a Formula

The following code sample reads data from another Historian Server using the Historian OLE DB provider, and stores it in a destination tag. When using this example, specify the username and password.
'connection and recordset variables
Dim Cnxn
Dim rsCurrentValueFromOtherServer
'open connection
Set Cnxn = CreateObject("ADODB.Connection")
'connect to default server using current username and password
'establish connection
Cnxn.Open "Provider=ihOLEDB.iHistorian.1;User Id=;Password="
'Create and open first Recordset using Connection execute
Set rsCurrentValueFromOtherServer = CreateObject("ADODB.Recordset")
'Get the value from the other server
Set rsCurrentValueFromOtherServer = Cnxn.Execute("select value from ihRawData 
where SamplingMode=CurrentValue and tagname = Simulation00001") 
'Set the result to the current value of other tag
Result=rsCurrentValueFromOtherServer("Value")
'Clean up
IF rsCurrentValueFromOtherServer.State = adStateOpen THEN
rsCurrentValueFromOtherServer.Close
END IF
IF Cnxn.State = adStateOpen THEN Cnxn.Close
END IF
Set rsCurrentValueFromOtherServer = Nothing
Set Cnxn = Nothing

Using a File

The following code sample shows how to read and write text files during a calculation. You may have data in a file to use as input to a calculation, or you may want to write debug values to a text file instead of using the LogMessage function.
Dim filesys, writefile, count,readfile
'need to create a file system object since there is no
'file I/O built into VBScript
Set filesys = CreateObject("Scripting.FileSystemObject")
'open the text file, or create it if it does not exist
set readfile = filesys.OpenTextFile("C:\somefile.txt", 1, true)
'try to read from the file
IF readfile.AtEndOfLine <> true THEN
count= readfile.ReadAll
END IF
'add one to the number stored in the count count = count+1
'close the file for reading 
readfile.Close
'open the same file but for writing
Set writefile= filesys.OpenTextFile("C:\somefile.txt", 2, true)
'write the updated count writefile.Write count
'close file for writing 
writefile.Close
Result = count

Converting a Number to a String

If your device and collector expose data as numeric codes, you can change to a string description. This examples also demonstrates that a calculation can output a string.
DIM X
x=CurrentValue ("tag1")
select case x
case 1
Result="one"
case 2
Result="two"
case else
Result="other"
End select

Detecting Recovery Mode Inside a Formula

The following code sample detects the recovery mode or recalculation inside a formula. If there are individual tags, you do not want to perform a recovery.
Dim MAXDIFF, TimeDiff
'Maximum difference in timestamps allowed (Must be > 2,
'units = seconds) MAXDIFF = 10
'Calculate time difference
TimeDiff = DateDiff("s", CurrentTime(), Now)
'Compare times, if difference is < MAXDIFF seconds perform calc
If TimeDiff < MAXDIFF Then
'Place calculation to be performed here:
Result = CurrentValue("DENALI.Simulation00001") Else
'Place what is to be done when no calc is performed here
Result = Null
End If

Looping Through Data Using the SDK

The following code sample uses the SDK to perform a query on a data set. It determines the minimum raw value over a one-hour time period.
on error resume next
Dim MyServer 'As Historian_SDK.Server
Dim I
Dim J
Dim K
Dim strComment
Dim lngInterval
Dim TagCount
Dim strDataQuality
Dim iDataRecordset
Dim iDataValue
Dim lEndTime, lStartTime, lNumSamples
Dim lNumSeconds, lNumSamplesPerSecond
Dim RawMin
'Instantiate The SDK
Set MyServer = CreateObject("iHistorian_SDK.Server")
'Attempt Connection
If Not MyServer.Connect("DENALI", "administrator","") Then
result = err.description
else
Set iDataRecordset = MyServer.Data.NewRecordset
'Find the number of samples.
'build query
With iDataRecordset
.Criteria.Tagmask = "EIGER.Simulation00001"
.Criteria.StartTime = DateAdd("h",-1,Now)
.Criteria.EndTime = Now
.Criteria.SamplingMode = 4 'RawByTime
.Criteria.Direction = 1 'forward
.Fields.AllFields
'do query
If Not .QueryRecordset Then
result = err.description
End If
'Some Large number so that real samples are less
RawMin = 1000000
For I = 1 To iDataRecordset.Tags.Count
For J = 1 To iDataRecordset.Item(I).Count
Set iDataValue = iDataRecordset.Item(I).Item(J)
' if the value is good data quality
if iDataValue.DataQuality = 1 then
if iDataValue.Value < RawMin then
rawMin = iDataValue.Value
end if
end if
lNumSamples = lNumSamples + 1
Next
Next
End With
End If
Result = RawMin
'Disconnect from server
MyServer.Disconnect

Using an ADO Query

The following code sample uses a query combining Historian data with ADO data. In the example, you convert a collected value, number of barrels per day (BarrelsUsedToday), to a dollar amount. The code then obtains the price per barrel (CostOfBarrel) from the SQL server, and finally stores the total dollars in an integer tag (TotalCostToday).

You can also do this with a linked server and the Historian OLE DB provider, but this example maintains a history of the results.

Dim CostOfBarrel, BarrelsUsedToday, TotalCostToday
'Calculate the total number of barrels used over
'the previous 24hours.
BarrelsUsedToday = Calculation("BarrelsUsedTag","Total","Now 1Day","Now",Quality)
'Retrieve cost per barrel used
Dim SQLExpression
Dim Cnxn
Dim rsCurrentValue
SQLExpression = "SELECT Barrel_Cost AS Value1 FROM RawMaterial_Costs WHERE Barrel_Type = CrudeOil and
samplingmode = CurrentValue"
'open connection
Set Cnxn = CreateObject("ADODB.Connection")
'connect to default server using current username and password
'establish connection
Cnxn.Open "Provider=SQLOLEDB.1;User ID=sa; Password=;Initial Catalog=Northwind"
'Create and open first Recordset using Connection execute
Set rsCurrentValue = CreateObject("ADODB.Recordset")
'Get the value from the other server
Set rsCurrentValue= Cnxn.Execute(SQLExpression)
'Set the result to the current value of other tag
CostOfBarrel = rsCurrentValue("Value1")
'Clean up
If rsCurrentValue.State = adStateOpen then 
rsCurrentValue.Close
End If
If Cnxn.State = adStateOpen then
Cnxn.Close
End If
Set rsCurrentValue = Nothing
Set Cnxn = Nothing
'Retrieve number of barrels used
BarrelsUsedToday = Calculation("BarrelsUsed","Count","Now 1Day","Now",Quality)
'Calculate total cost of barrels today
TotalCostToday = CostOfBarrel * BarrelsUsedToday

Windows Performance Statistics Physical Memory Usage

The following code sample creates a formula that collects data reflecting private byte usage.
`Get a reference to the local data archiver process object
Set RawProc = GetObject("winmgmts:Win32_PerfRawdata_Perfproc_process.name='ihDataArchiver.'")
`Scale the virtual bytes number to a value within
`the tag's EGU range
result =RawProc.PrivateBytes *.001

Windows Performance Statistics Virtual Memory Usage

The following code sample creates a formula that collects data reflecting virtual byte usage.
`Get a reference to the local data archiver process object
                Set RawProc = GetObject("winmgmts:Win32_PerfRawdata_Perfproc_process.name='ihDataArchiver.'")
`Scale the virtual bytes number to a value within the
`tag's EGU range
result =RawProc.VirtualBytes *.0001

Determining Collector Downtime

The following code sample determines the amount of downtime, in seconds, that the Calculation collector has experienced over the last day. Downtime occurs when there are two consecutive bad quality data points for the pulse tag. If the last known data point for the pulse tag is bad quality, all the time between its timestamp and the current time is regarded as downtime. In the following sample, the pulse tag is configured to be polled, with a collection interval of one day.
Dim pulseTag, totalDownTime, startTime, endTime
Dim prevTime, prevQuality, lastPrevTime, lastPrevQuality 
pulseTag = "calcPulseTag"
totalDownTime = 0 
endTime = CurrentTime()
startTime = DateAdd("d", -1, endTime)
lastPrevTime = curTime lastPrevQuality = 0
Do
  'get the timestamp and quality of the tag value previous to the last one we checked
  On Error Resume Next
  prevTime = PreviousTime(pulseTag, lastPrevTime) 
  If Err.Number <> 0 Then
    'no more values for this tag exit gracefully
    Exit Do
End If
prevQuality = PreviousQuality(pulseTag, lastPrevTime)
'if we have two consecutive bad data points, add to the downtime
If prevQuality = 0 And lastPrevQuality = 0 Then
  If prevTime > startTime Then
    totalDownTime = totalDownTime + DateDiff("s", prevTime, lastPrevTime) 
Else
    totalDownTime = totalDownTime + DateDiff("s", startTime, lastPrevTime) 
End If
End If
  'store the timestamp and quality for comparison with the next values 
lastPrevQuality = prevQuality
  lastPrevTime = prevTime
Loop While lastPrevTime > startTime
Result = totalDownTime

Analyzing the Collected Data

The following code sample analyzes the collected data to determine the amount of time that a condition was true and had good quality in the last day.
Dim tagName, startTime, endTime
tagName = "testTag"
startTime = "Now 1Day" 
endTime = "Now"
Result = CalculationFilter(tagName, "TotalTimeGood", startTime, endTime, 100, tagName, "AfterTime", "Equal", 1)

Simulating Demand Polling

To simulate demand polling, create the following tags.
Tag Description
Polled Tag A polled tag with a collection interval of the longest period you want between raw samples. Do not enable collector or archive compression. This tag should point to the same source address as the unsolicited tag.
Unsolicited Tag An unsolicited tag with a 0 or 1 second collection interval. This tag ensures you will be notified whenever changes occur. This tag should point to the same source address as the polled tag.
Combined Tag An unsolicited calculation tag that is triggered by either the polled tag or the unsolicited tag, and combines the raw samples of both into a single tag. Use a 0 or 1 second collection interval and use the following formula:
dim timetag1 
dim timetag2 
dim tag1
dim tag2
Dim x
tag1 = "T20.di-1.F_CV"
tag2 = "t20.T20.DI-1.F_CV"
x = DateAdd("s", 1,CurrentTime) ' add 1 second to calc time
' Get the timestamp of the newest raw sample for tag1:
timetag1 = previousTime(tag1, x)
' Get the timestamp of the newest raw sample for tag2:
timetag2 = previousTime(tag2, x)
if timetag1 > timetag2 then
' If tag1 triggered me, then:
result = PreviousValue(tag1,CurrentTime)
else
' If tag2 triggered me, then:
result = PreviousValue(Tag1, CurrentTime)
end if