Major upgrade
This commit is contained in:
334
Source/Program/TestStand/TestStand.ProgramManager.cs
Normal file
334
Source/Program/TestStand/TestStand.ProgramManager.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
// UNCLASSIFIED
|
||||
/*-------------------------------------------------------------------------
|
||||
RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
|
||||
PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
|
||||
AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
|
||||
UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
|
||||
RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
|
||||
CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
|
||||
OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
|
||||
COMPANY.
|
||||
|
||||
THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
|
||||
GOVERNMENT.
|
||||
|
||||
UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
||||
-------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using NationalInstruments.TestStand.Interop.API;
|
||||
using ProgramLib;
|
||||
|
||||
namespace TestStand
|
||||
{
|
||||
internal class OriginalStepSetting
|
||||
{
|
||||
public int _stepIndex;
|
||||
public StepGroups _stepGroup;
|
||||
public string _postExpression;
|
||||
public bool _clearAdditionalResults;
|
||||
|
||||
public OriginalStepSetting(int stepIndex, StepGroups stepGroups, string postExpression, bool clearAdditionalResults = false)
|
||||
{
|
||||
_stepIndex = stepIndex;
|
||||
_stepGroup = stepGroups;
|
||||
_postExpression = postExpression;
|
||||
_clearAdditionalResults = clearAdditionalResults;
|
||||
}
|
||||
}
|
||||
|
||||
internal class StepErrorSetting
|
||||
{
|
||||
public int _stepIndex;
|
||||
public StepGroups _stepGroup;
|
||||
public string _errorMessage;
|
||||
|
||||
public StepErrorSetting(int stepIndex, StepGroups stepGroups, string errorMessage)
|
||||
{
|
||||
_stepIndex = stepIndex;
|
||||
_stepGroup = stepGroups;
|
||||
_errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide wrapper function to intialize and finalize Program class
|
||||
/// All methods defined in this class must be called from TestStand.
|
||||
/// No other code should be calling these methods outside of TestStand.
|
||||
/// </summary>
|
||||
public static class ProgramManager
|
||||
{
|
||||
// keep track of all the steps that have their settings changed by this program
|
||||
private static List<OriginalStepSetting> _originalStepSettingList = new List<OriginalStepSetting>();
|
||||
|
||||
// keep track of all the steps that have errors
|
||||
private static List<StepErrorSetting> _stepErrorSettingList = new List<StepErrorSetting>();
|
||||
|
||||
/// <summary>
|
||||
/// Initialize power supply measurement manager
|
||||
/// </summary>
|
||||
/// <param name=""></param>
|
||||
/// <returns></returns>
|
||||
public static void InitializeProgram(string partNumber = "", string serialNumber = "", string testType = "", string testName = "", bool isThereHardware = true, string configSubFolderName = "Default", object testStandSeqContext = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_originalStepSettingList = new List<OriginalStepSetting>();
|
||||
_stepErrorSettingList = new List<StepErrorSetting>();
|
||||
ProgramLib.Program.Instance(partNumber, serialNumber, testType, testName, isThereHardware, configSubFolderName, testStandSeqContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// DO NOT THROW in this block
|
||||
// this function will handle the error accordingly since we could be calling this from third-party test executive like TestStand
|
||||
TerminateTestOnMainThreadError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize power supply measurement manager
|
||||
/// </summary>
|
||||
/// <param name=""></param>
|
||||
/// <returns></returns>
|
||||
public static void FinalizeProgram(bool openTestDataFolder = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ProgramLib.Program._program != null)
|
||||
{
|
||||
// signal to all running threads to exit
|
||||
ProgramLib.Program._program.EventManager[EventManager.Events.GLOBAL_QUIT].Set();
|
||||
|
||||
BasicAction action = new UutPowerOffAction();
|
||||
action.Run();
|
||||
|
||||
// needs to kill all the support threads since they could be accessing GUI
|
||||
ProgramLib.Program._program.KillSupportThreads();
|
||||
|
||||
// must kill GUI thread before Program destructor can get called
|
||||
ProgramLib.Program._program.KillGuiThread();
|
||||
|
||||
if (ProgramLib.Program.Instance().MalMeasurementLibManager.SwitchMeasurementManager != null)
|
||||
{
|
||||
ProgramLib.Program.Instance().MalMeasurementLibManager.SwitchMeasurementManager.Dispose();
|
||||
}
|
||||
|
||||
PerformTestStandEndOfRunActivities();
|
||||
|
||||
ProgramLib.Program._program.PerformPostTestActivities();
|
||||
|
||||
if (openTestDataFolder)
|
||||
Process.Start(@$"{ProgramLib.Program._program.FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST)}");
|
||||
|
||||
// this starts garbage collection on this object which then in turn start garbage collection
|
||||
// on all objects it created
|
||||
ProgramLib.Program._program = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// DO NOT THROW in this block
|
||||
// this function will handle the error accordingly since we could be calling this from third-party test executive like TestStand
|
||||
TerminateTestOnMainThreadError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform TestStand End-of-Run activities
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static void PerformTestStandEndOfRunActivities()
|
||||
{
|
||||
if (ProgramLib.Program.TestStandSeqContext != null)
|
||||
{
|
||||
// restore original settings for steps that have been modified by this program
|
||||
foreach (OriginalStepSetting setting in _originalStepSettingList)
|
||||
{
|
||||
ProgramLib.Program.TestStandSeqContext.Sequence.GetStep(setting._stepIndex, setting._stepGroup).PostExpression = setting._postExpression;
|
||||
|
||||
if (setting._clearAdditionalResults)
|
||||
ProgramLib.Program.TestStandSeqContext.Sequence.GetStep(setting._stepIndex, setting._stepGroup).AdditionalResults.CustomResults.Clear();
|
||||
}
|
||||
|
||||
// for the first step that has an error in the Main group but is allowed to continue,
|
||||
// we want to flag it at the end of the sequence
|
||||
if (!ProgramLib.Program._terminateTestInitiated)
|
||||
{
|
||||
int numSteps = ProgramLib.Program.TestStandSeqContext.Sequence.GetNumSteps(StepGroups.StepGroup_Main);
|
||||
bool foundError = false;
|
||||
for (int i = 0; i < numSteps; i++)
|
||||
{
|
||||
foreach (StepErrorSetting setting in _stepErrorSettingList)
|
||||
{
|
||||
if (setting._stepIndex == i && setting._stepGroup == StepGroups.StepGroup_Main)
|
||||
{
|
||||
Step step = ProgramLib.Program.TestStandSeqContext.Sequence.GetStep(setting._stepIndex, setting._stepGroup);
|
||||
|
||||
if (String.Equals(step.ResultStatus, "Error"))
|
||||
{
|
||||
// tells teststand there's a exception occurred and give it the error message
|
||||
ProgramLib.Program.TestStandSeqContext.SequenceErrorMessage = setting._errorMessage + " ";
|
||||
ProgramLib.Program.TestStandSeqContext.SequenceErrorOccurred = true;
|
||||
foundError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundError)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function is only meant to be called from third-party test executive such as TestStand.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static void MoveTestStandTestReportToTestFolder(string sourceTestReportFilePath, string destinationTestReportPath)
|
||||
{
|
||||
string filenameWithoutExt = Path.GetFileNameWithoutExtension(sourceTestReportFilePath);
|
||||
string pdfFilename = filenameWithoutExt + ".pdf";
|
||||
sourceTestReportFilePath = Path.Combine(Path.GetDirectoryName(sourceTestReportFilePath), pdfFilename);
|
||||
string destinationTestReportFilePath = Path.Combine(destinationTestReportPath, pdfFilename);
|
||||
|
||||
// wait until the teststand report is created
|
||||
int maxRetries = 5;
|
||||
int retries = 0;
|
||||
while (retries++ < maxRetries && !File.Exists(sourceTestReportFilePath))
|
||||
{
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
if (File.Exists(sourceTestReportFilePath) && Directory.Exists(destinationTestReportPath))
|
||||
{
|
||||
File.Move(sourceTestReportFilePath, destinationTestReportFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
string reportDir = Path.GetDirectoryName(sourceTestReportFilePath);
|
||||
if (Regex.IsMatch(reportDir, @"\\report\\?$", RegexOptions.IgnoreCase))
|
||||
{
|
||||
Directory.Delete(Path.GetDirectoryName(sourceTestReportFilePath), true);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All code module should not throw exception into teststand and let teststand handle the exception
|
||||
/// We give the teststand step a nice error message and tell teststand to go to cleanup
|
||||
/// If we are not calling into code from teststand, then we throw exception as usual
|
||||
/// Call this only from code executing on the main TestStand thread
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
/// <returns></returns>
|
||||
internal static void TerminateTestOnMainThreadError(Exception ex, bool continueOnError = false)
|
||||
{
|
||||
if (ProgramLib.Program.TestStandSeqContext != null)
|
||||
{
|
||||
string message = ProgramLib.Util.LogException(ex, ProgramLib.Program._logger);
|
||||
message = Regex.Replace(message, "[\"]+", "");
|
||||
message = Regex.Replace(message, @"\\([nrt])", @"\\$1", RegexOptions.IgnoreCase);
|
||||
|
||||
lock (ProgramLib.Program._terminateTestSyncObj)
|
||||
{
|
||||
if (!ProgramLib.Program._terminateTestInitiated)
|
||||
{
|
||||
if (!continueOnError)
|
||||
{
|
||||
ProgramLib.Program.TestStandSeqContext.Step.AsPropertyObject().SetValBoolean("Result.Error.Occurred", 0, true);
|
||||
ProgramLib.Program._terminateTestInitiated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool foundError = false;
|
||||
foreach (StepErrorSetting setting in _stepErrorSettingList)
|
||||
{
|
||||
if (setting._stepIndex == ProgramLib.Program.TestStandSeqContext.Step.StepIndex && setting._stepGroup == ProgramLib.Program.TestStandSeqContext.Step.StepGroup)
|
||||
{
|
||||
setting._errorMessage = message;
|
||||
foundError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundError)
|
||||
_stepErrorSettingList.Add(new StepErrorSetting(ProgramLib.Program.TestStandSeqContext.Step.StepIndex, ProgramLib.Program.TestStandSeqContext.Step.StepGroup, message));
|
||||
}
|
||||
|
||||
ProgramLib.Program.TestStandSeqContext.Step.AsPropertyObject().SetValString("Result.Error.Msg", 0, $"{message} ");
|
||||
ProgramLib.Program.TestStandSeqContext.Step.ResultStatus = "Error";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Check inner exception.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Support threads such as monitor threads need a way to stop the sequence if it encounters a fatal error
|
||||
/// This function is meant to be called from background threads such as FailureMonitorThread which monitors any error signaled by any
|
||||
/// other background thread.
|
||||
/// </summary>
|
||||
/// <param name=""></param>
|
||||
/// <returns></returns>
|
||||
internal static void TerminateTestOnSupportThreadError()
|
||||
{
|
||||
if (ProgramLib.Program.TestStandSeqContext != null)
|
||||
{
|
||||
string message = ProgramLib.Util.LogException(ProgramLib.Program._fatalFailureExceptionFromSupportThread, ProgramLib.Program._logger);
|
||||
message = Regex.Replace(message, "[\"]+", "");
|
||||
message = Regex.Replace(message, @"\\([nrt])", @"\\$1", RegexOptions.IgnoreCase);
|
||||
|
||||
lock (ProgramLib.Program._terminateTestSyncObj)
|
||||
{
|
||||
if (!ProgramLib.Program._terminateTestInitiated)
|
||||
{
|
||||
// tells teststand there's a exception occurred and give it the error message
|
||||
ProgramLib.Program.TestStandSeqContext.SequenceErrorMessage = message + " ";
|
||||
ProgramLib.Program.TestStandSeqContext.SequenceErrorOccurred = true;
|
||||
|
||||
// tells TestStand to go to clean up
|
||||
ProgramLib.Program.TestStandSeqContext.StepGroup = StepGroups.StepGroup_Cleanup;
|
||||
ProgramLib.Program.TestStandSeqContext.NextStepIndex = 0;
|
||||
|
||||
ProgramLib.Program._terminateTestInitiated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save original step settings so we can restore them.
|
||||
/// If we don't restore step settings that we change in this program, then the sequence file will be marked as unsaved
|
||||
/// </summary>
|
||||
internal static void SaveOriginalStepSetting(int stepIndex, StepGroups stepGroup, string postExpression, bool clearAdditionResults = false)
|
||||
{
|
||||
bool stepAlreadyExist = false;
|
||||
foreach (OriginalStepSetting setting in _originalStepSettingList)
|
||||
{
|
||||
if (setting._stepIndex == stepIndex && setting._stepGroup == stepGroup)
|
||||
{
|
||||
stepAlreadyExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stepAlreadyExist)
|
||||
{
|
||||
_originalStepSettingList.Add(new OriginalStepSetting(stepIndex, stepGroup, postExpression, clearAdditionResults));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user