335 lines
14 KiB
C#
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));
|
|
}
|
|
}
|
|
}
|
|
}
|