sraposo

Programmatic approach to scheduling recalculations for analyses

Blog Post created by sraposo on Mar 11, 2020

Asset Analytics introduced an automatic recalculation feature a long time ago (2017 R2 release). This feature was great to automate the recalculation of some analyses for which the inputs receive out of order data. Ideally, out of order data streams should be fixed at the source, as out of order data can be bad for performance of the entire PI System. Some out of order data streams can't be fixed at the source, and in those cases automatic recalculation is a convenient feature to use. There exists 1 major gap with the automatic recalculation feature, Event Frame analyses are not supported.You can up vote the request here to have this feature in a future version. 

 

This blog post is to help those that have: 

  • Out of order data streams that can't be fixed at the source. 
  • These streams write to PI Points that are used as Event Frame triggers.
  • Manual recalculation of these analyses isn't a great solution, an automated approach is needed. 

 

If you are in either of the below situations, there are much better solutions than the contents of this blog post: 

  • Asset Analytics is older than 2017 R2 and you want to automate the recalculation of non Event Frame analyses. => Upgrade.
  • You're not sure if the out of order data stream can be fixed. This blog post is easier to follow than figuring out if the data stream can be fixed or not. => Fix the data stream if you can. It's much better to fix a problem at the source, then try to account from it up stream. 

 

The below PowerShell script is not a complete solution. It should definitely be improved before being deployed in production. Here are some improvements that could be made: 

 

  1. Add an error logging function to redirect error messages and exceptions to a text file. 
  2. Add a parameter for the type of recalculation that needs to be done (backfill or recalculation). 
  3. Modify the $AnalysisTemplateName parameter to be optional and add some additional optional parameters. For example, Analysis Category could be used as a filter. 

 

There are of course several other improvements that can be made. Also, please keep in mind that I am by no means a PowerShell expert. If you notice something in the script that isn't following best practices, please correct me! This is a good learning opportunity for myself as well  ! 

 

The below script will search for all analyses in a given AF Server and AF Database based on a specific Analysis Template. It will then queue a recalculation from the input start and end time. For any information on any of the methods used, please refer to our AF SDK documentation: 

 

AF SDK Overview 

 

You can use Task Scheduler to schedule the script: Configure to run a PowerShell Script into Task Scheduler - TechNet Articles - United States (English) - TechNet Wiki 

 

Here is an example of how to input the parameter values: 

 

 

<# The below example is offered as is, for more
information refer to: Apache License, Version 2.0
Questions in the comment section of the PI Square blog post
associated to this script are welcomed!#>


param (
[Parameter(Mandatory)]
[string]$AFServerName,
[Parameter(Mandatory)]
[string]$DatabaseName,
[Parameter(Mandatory)]
[string]$AnalysisTemplateName,
[Parameter(Mandatory)]
[string]$StartTimeString,
[Parameter(Mandatory)]
[string]$EndTimeString
)

try
{
[Reflection.Assembly]::LoadWithPartialName("OSIsoft.AFSDK") | Out-Null
}
catch
{
Write-Error "AF SDK cannot be loaded, make sure this script is running on a machine with AF Client installed."
}


#Check input parameters

$startTime = [OSIsoft.AF.Time.AFTime]::new($null)
$endTime = [OSIsoft.AF.Time.AFTime]::new($null)


if (![OSIsoft.AF.Time.AFTime]::TryParse($StartTimeString, [ref]$startTime))
{
Write-Error "Input Start Time can't be parsed. Check Format"
}

if (![OSIsoft.AF.Time.AFTime]::TryParse($EndTimeString, [ref]$endTime))
{
Write-Error "Input End Time can't be parsed. Check Format"
}

$timeRange = [OSIsoft.AF.Time.AFTimeRange]::Parse($startTime,$endTime)

[OSIsoft.AF.PISystems] $afServers = New-Object OSIsoft.AF.PISystems

[OSIsoft.AF.PISystem] $afServer = $afServers[$AFServerName]

try
{
if ($afServer -eq $null)
{
Write-Error "AFServer name specified is invalid."
Exit
}


}
catch
{
Write-Error "Can't connect to AF Server. "
Exit
}

[OSIsoft.AF.AFDatabase] $DB = $afServer.Databases[$DatabaseName]

try
{
if ($DB -eq $null)
{
Write-Error "AF Database name specified is invalid."
Exit
}


}
catch
{
Write-Error "Can't connect to AF Database. "
Exit
}

[OSIsoft.AF.Search.AFAnalysisSearch] $analysisSearch = [OSIsoft.AF.Search.AFAnalysisSearch]::new($DB, "myAnalysisSearch", "Template:'$AnalysisTemplateName'")

try
{
$analyses = $analysisSearch.FindObjects()
}
catch
{
Write-Error "There was an issue searching for analyses based of the template: $AnalysisTemplateName. Is there a typo in the template name?"
Exit

}

#Now that inputs have been checked and analyses retrieved,

Try
{
[string] $reasonCode = ""
if(!$afServer.AnalysisService.CanQueueCalculation([ref] $reasonCode )) #check to see if we can queue recalculations
{
Write-Error "Can't queue any recalculation request. Reason is: $reasonCode"
}


$recalcGuid = $afServer.AnalysisService.QueueCalculation($analyses,$timeRange,1) #Assume recalculation (replace values). Queue analyses
#Could output the recalcGuid to a log file

}
catch
{

Write-Error "Can't connect to the PI Analysis Service. Ensure the PI Analysis Service is running and a connction from PSE works."
Exit
}

Outcomes