Files
GenericTeProgramLibrary/Source/Program/TestStand/TestStand.ProgramManager.cs
2025-10-24 15:18:11 -07:00

335 lines
14 KiB
C#

/*-------------------------------------------------------------------------
// 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));
}
}
}
}