/// <summary> /// Run calculations for specific time stamps. /// </summary> /// inspired from: /// <see cref="https://pisquare.osisoft.com/message/28537#28537"/> /// <param name="analysis">Analysis that needs to be evaluated</param> /// <param name="times">List of times</param> /// <param name="evaluationTime">output, contains the time taken to execute all the calculations</param> /// <param name="evaluationsErrorsCount">output, contains the number of evaluations in error.</param> /// <returns></returns> public List <AFValue> Run(AFAnalysis analysis, IEnumerable <AFTime> times, out TimeSpan evaluationTime, out int evaluationsErrorsCount) { evaluationsErrorsCount = 0; var results = new List <AFValue>(); var stopwatch = Stopwatch.StartNew(); var analysisConfiguration = analysis.AnalysisRule.GetConfiguration(); var state = new AFAnalysisRuleState(analysisConfiguration); foreach (var time in times) { // Console.WriteLine("Evaluating for {0}", time); state.Reset(); state.SetExecutionTimeAndPopulateInputs(time); analysis.AnalysisRule.Run(state); if (state.EvaluationError != null) { if (_debug) { _logger.ErrorFormat("Analyse in error at time: {3:s} - {0} - at {1}, Error: {2}", analysis.Name, analysis.GetPath(), state.EvaluationError, time); } evaluationsErrorsCount += 1; } // this merges the state (results) with the configuration so its easier to loop with both... var resultSet = analysisConfiguration.ResolvedOutputs.Zip(state.Outputs, Tuple.Create); foreach (var result in resultSet) { // for more clarty, we take out our data into clearer variables AFAnalysisRuleResolvedOutput analysisRow = result.Item1; var calcRes = (AFValue)result.Item2; // we filter to get only the results that have an output attribute ( an AFValue ) if (analysisRow.Attribute != null) { // add new AF Value into the results table results.Add(new AFValue((AFAttribute)analysisRow.Attribute, calcRes.Value, calcRes.Timestamp)); } } } stopwatch.Stop(); evaluationTime = stopwatch.Elapsed; return(results); }
/// <summary> /// Adapted from Mike's example /// Mike also talks about errors and warnings in his post, you should check it. /// </summary> /// <see cref="https://pisquare.osisoft.com/message/28537#28537"/> /// <param name="analysis"></param> /// <param name="times"></param> /// <returns></returns> private static List <AFValue> Calculate(AFAnalysis analysis, IEnumerable <AFTime> times) { var results = new List <AFValue>(); var analysisConfiguration = analysis.AnalysisRule.GetConfiguration(); var state = new AFAnalysisRuleState(analysisConfiguration); foreach (var time in times) { //Console.WriteLine("Evaluating for {0}", time); state.Reset(); state.SetExecutionTimeAndPopulateInputs(time); analysis.AnalysisRule.Run(state); if (state.EvaluationError == null) { // this merges the state (results) with the configuration so its easier to loop with both... var resultSet = analysisConfiguration.ResolvedOutputs.Zip(state.Outputs, Tuple.Create); foreach (var result in resultSet) { // for more clarty, we take out our data into clearer variables AFAnalysisRuleResolvedOutput analysisRow = result.Item1; var calcRes = (AFValue)result.Item2; // we filter to get only the results that have an output attribute ( an AFValue ) if (analysisRow.Attribute != null) { // add new AF Value into the results table results.Add(new AFValue((AFAttribute)analysisRow.Attribute, calcRes.Value, calcRes.Timestamp)); } } } else { // errors occur quite frequently, for example, TagTot('attr1', 't', '*') will fail if computed at '*' = 't' // but this does not occur when the analysis is running on event base. //Console.WriteLine("An error occurred: {0}", state.EvaluationError.Message); } } return(results); }