Major upgrade

This commit is contained in:
Duc
2025-10-24 15:18:11 -07:00
parent fd85735c93
commit ce583d1664
478 changed files with 237518 additions and 47610 deletions

View File

@@ -16,60 +16,88 @@ GOVERNMENT.
UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using MeasurementManagerLib;
using NationalInstruments.TestStand.Interop.API;
using NLog;
using Raytheon.Instruments;
using NLog.Targets;
using NLog.Targets.Wrappers;
using Raytheon.Common;
using MeasurementManagerLib;
using Raytheon.Common.PdelWriter;
using Raytheon.Common.PdelWriter.Utilities;
using Raytheon.Instruments;
namespace ProgramLib
{
/// <summary>
/// Class for interfacing with any Test Executive such as third party test executive such as Test Stand
/// Class for interfacing with any third party test executive such as Test Stand
/// DO NOT implement IDisposable interface for this class. If there are unmanaged resources that need to be freed,
/// do it in the destructor.
/// </summary>
public partial class Program
/// </summary>
internal partial class Program
{
#region PrivateMemberVariables
// the one and only program
private static Program _program;
internal static Program _program = null;
private object _terminateTestSyncObj = new object();
private bool _terminateTestInitiated = false;
// simulation
private readonly bool _isThereHardware;
// file management
private string _partNumber;
private string _serialNumber;
internal static object _terminateTestSyncObj = new object();
internal static bool _terminateTestInitiated = false;
private List<BasicThread> _threadList = new List<BasicThread>();
private IInstrumentManager _instrumentManager;
private ILogger _logger;
internal static ILogger _logger;
private object _fatalErrorMsgFromSupportThreadSyncObj = new object();
private string _fatalErrorMsgFromSupportThread;
private object _fatalFailureExceptionFromSupportThreadSyncObj = new object();
internal static Exception _fatalFailureExceptionFromSupportThread;
private MalMeasurementLibManager _malMeasurementLibManager;
#endregion
internal SequenceContext TestStandSeqContext { get; private set; }
internal IInstrumentManager InstrumentManager
{
get { return _instrumentManager; }
set { _instrumentManager = value; }
}
internal bool IsThereHardware { get; private set; }
internal bool IsAppControlled { get; private set; }
internal bool CableSelfTestHasRun { get; set; }
internal static SequenceContext TestStandSeqContext { get; private set; }
internal UutInfo UutInfo { get; private set; }
internal EventManager EventManager { get; private set; }
internal FileAndFolderManager FileAndFolderManager { get; set; }
internal IConfigurationFile ProgramConfig { get; private set; }
internal MalMeasurementLibManager MalMeasurementLibManager { get; private set; }
internal string TestMethodConfigFilePath { get; private set; }
internal TestInfo TestInfo { get; private set; }
internal EventManager EventManager { get; private set; }
internal FileAndFolderManager FileAndFolderManager { get; private set; }
internal IConfigurationFile ProgramGeneralConfig { get; private set; }
internal IConfigurationFile ProgramSpecificConfig { get; private set; }
internal TestStation TestStation { get; private set; }
internal EthernetSocketManager EthernetSocketManager { get; private set; }
internal PdelInformation PdelData { get; private set; }
internal TestResultList PcodeTestResultList { get; private set; }
internal bool SttoSuccess { get; set; }
internal MalMeasurementLibManager MalMeasurementLibManager
{
get
{
if (_malMeasurementLibManager == null)
throw new MalMeasurementManagerNullReferenceException(typeof(MalMeasurementLibManager));
return _malMeasurementLibManager;
}
private set
{
_malMeasurementLibManager = value;
}
}
internal List<string> PowerModulesToBePowered { get; set; }
/// <summary>
/// Create an instance of this class. Only one instance is allowed
@@ -78,100 +106,168 @@ namespace ProgramLib
/// <param name="serialNumber">The UUT serial number</param>
/// <param name="isThereHardware">false for simulation</param>
/// <returns></returns>
public static Program Instance(string partNumber = "", string serialNumber = "", bool isThereHardware = true, object testStandSeqContext = null)
internal static Program Instance(string partNumber = "", string serialNumber = "", string testType = "", string testName = "", bool isThereHardware = true, string configSubFolderName = "Default", object testStandSeqContext = null)
{
if (testStandSeqContext != null)
{
TestStandSeqContext = testStandSeqContext as SequenceContext;
}
if (_logger == null)
{
// this line is for when this is called from third-party test executive such as TestStand. Without this, NLOG library won't be able to find the NLog.config file
NLog.LogManager.Setup().LoadConfigurationFromFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "NLog.config"));
_logger = LogManager.GetCurrentClassLogger();
}
if (_program == null)
{
_program = new Program(partNumber, serialNumber, isThereHardware, testStandSeqContext);
_program = new Program(partNumber, serialNumber, testType, testName, isThereHardware, configSubFolderName);
_program.Initialize();
}
return _program;
}
/// <summary>
/// This function is only meant to be called from third-party test executive such as TestStand.
/// By settting this object to null, Garbage collection is initiated on this objects and any objects created by this object
/// with the exception of static objects. Static objects needs to be explicitly killed by setting them to null
/// </summary>
/// <returns></returns>
public static void KillInstance()
{
if (_program != null)
{
// signal to all running threads to exit
_program.EventManager[EventManager.Events.GLOBAL_QUIT].Set();
UutPowerAction uutPowerAction = new UutPowerAction();
uutPowerAction.UutPowerOff();
// needs to kill all the support threads since they could be accessing GUI
_program.KillSupportThreads();
// Because the GuiManager class starts a STA thread to manage WPF GUIs
// the destructor doesn't get called if we don't shut down the STA thread first
// so we explicitly call Dispose here to shutdown the thread. This is a special case.
// All other classes call Dispose() in their Destructor
_program.GetGuiManager()?.Dispose();
// this starts garbage collection on this object which then in turn start garbage collection
// on all objects it created
_program = null;
}
}
/// <summary>
/// The private constructor
/// </summary>
/// <param name="partNumber">the UUT part number</param>
/// <param name="serialNumber">the UUT serial number</param>
/// <param name="isThereHardware">false for simulation</param>
private Program(string partNumber, string serialNumber, bool isThereHardware, object testStandSeqContext)
private Program(string partNumber, string serialNumber, string testType, string testName, bool isThereHardware, string configSubFolderName)
{
// this line is for when this is called from third-party test executive such as TestStand. Without this, NLOG library won't be able to find the NLog.config file
NLog.LogManager.Setup().LoadConfigurationFromFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "NLog.config"));
_logger = LogManager.GetCurrentClassLogger();
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
ProgramConfig = new ConfigurationFile(Path.Combine(assemblyFolder, GeneralConstants.ProgramConfigFilename));
TestMethodConfigFilePath = Path.Combine(assemblyFolder, GeneralConstants.TestMethodConfigFolderName, GeneralConstants.TestMethodConfigFileName);
EventManager = new EventManager();
FileAndFolderManager = new FileAndFolderManager(ProgramConfig);
if (testStandSeqContext != null)
{
TestStandSeqContext = testStandSeqContext as SequenceContext;
}
// make sure PN/SN are valid (TS may not pass in the PN)
if (partNumber == "" || partNumber == null)
{
partNumber = "DefaultPN";
}
if (serialNumber == "" || serialNumber == null)
{
serialNumber = "DefaultSN";
}
_partNumber = partNumber;
_serialNumber = serialNumber;
_isThereHardware = isThereHardware;
SttoSuccess = false;
// Initialze all other configuration that the program needs
UutInfo = new UutInfo(_partNumber, _serialNumber);
try
{
var configFolder = Path.Combine(assemblyFolder, Raytheon.Common.Constants.InstrumentConfigFolder);
_instrumentManager = new GeneralInstrumentManager(assemblyFolder, configFolder, _isThereHardware);
PdelData = new PdelInformation();
PcodeTestResultList = new TestResultList();
IsThereHardware = isThereHardware;
CableSelfTestHasRun = false;
_terminateTestInitiated = false;
_fatalFailureExceptionFromSupportThread = null;
string testOperator = Util.GetWindowsUserFullName(Environment.UserDomainName, Environment.UserName);
DateTime assemblyBuildDateTime = File.GetLastWriteTime(GetType().Assembly.Location);
string assemblyBuildDateTimeStr = assemblyBuildDateTime.ToString("MM/dd/yyyy");
Version appVersion = GetType().Assembly.GetName().Version;
string softwareBuildInfo = $"{assemblyBuildDateTimeStr} - Version " + appVersion.Major.ToString() + "." + appVersion.Minor.ToString();
if (String.IsNullOrEmpty(testOperator))
testOperator = Environment.UserName;
if (TestStandSeqContext != null)
{
partNumber = TestStandSeqContext.FileGlobals.GetValString("UUT.PartNumber", PropertyOptions.PropOption_CaseInsensitive);
serialNumber = TestStandSeqContext.FileGlobals.GetValString("UUT.SerialNumber", PropertyOptions.PropOption_CaseInsensitive);
testType = TestStandSeqContext.FileGlobals.GetValString("UUT.AdditionalData.TEST_CATEGORY", PropertyOptions.PropOption_CaseInsensitive);
if (String.Equals(testType, "engineering", StringComparison.OrdinalIgnoreCase))
{
testType = "en";
}
else
testType = "at";
testName = TestStandSeqContext.FileGlobals.GetValString("UUT.AdditionalData.TEST_NAME", PropertyOptions.PropOption_CaseInsensitive);
if (!String.IsNullOrEmpty(testOperator))
TestStandSeqContext.FileGlobals.SetValString("UUT.AdditionalData.TEST_OPERATOR", PropertyOptions.PropOption_CaseInsensitive, testOperator);
else
testOperator = TestStandSeqContext.FileGlobals.GetValString("UUT.AdditionalData.TEST_OPERATOR", PropertyOptions.PropOption_CaseInsensitive);
string dateTimeStr = TestStandSeqContext.Root.Locals.GetValString("StartDate.ShortText", PropertyOptions.PropOption_CaseInsensitive);
dateTimeStr += " " + TestStandSeqContext.Root.Locals.GetValString("StartTime.Text", PropertyOptions.PropOption_CaseInsensitive);
TestInfo = new TestInfo(testType, testName, testOperator, DateTime.Parse(dateTimeStr));
TestStandSeqContext.FileGlobals.SetValString("UUT.AdditionalData.MTS_Software_Build", PropertyOptions.PropOption_CaseInsensitive, softwareBuildInfo);
}
else
{
// make sure PN/SN are valid (TS may not pass in the PN)
if (String.IsNullOrEmpty(partNumber))
{
partNumber = "DefaultPN";
}
if (String.IsNullOrEmpty(serialNumber))
{
serialNumber = "DefaultSN";
}
if (String.Equals(testType, "en", StringComparison.OrdinalIgnoreCase) || String.Equals(testType, "at", StringComparison.OrdinalIgnoreCase))
{
TestInfo = new TestInfo(testType, testName, testOperator, DateTime.Now);
}
else
{
throw new Exception("Argument testType is invalid. Must be either 'en' or 'at'");
}
if (String.IsNullOrEmpty(testName))
{
throw new Exception("Argument testName is invalid. Please provide name of test");
}
}
PdelData.TestOperator = testOperator;
PdelData.TestSoftwareIdentification = softwareBuildInfo;
PdelData.UutIdentification = partNumber;
PdelData.UutSerialNumber = serialNumber;
PdelData.TestStartTime = TestInfo.TestStartDateTime;
PdelData.TestCategory = TestCategory.ENGINEERING_TEST;
if (TestInfo.TestType == "at")
{
PdelData.TestCategory = TestCategory.ACCEPTANCE_TEST;
}
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
ProgramGeneralConfig = new ConfigurationFile(Path.Combine(assemblyFolder, GeneralConstants.ConfigFolderName, GeneralConstants.ProgramGeneralConfigFilename));
PowerModulesToBePowered = new List<string>();
EventManager = new EventManager();
IConfigurationFile programSpecificConfig;
FileAndFolderManager = new FileAndFolderManager(ProgramGeneralConfig, out programSpecificConfig, isThereHardware, configSubFolderName);
ProgramSpecificConfig = programSpecificConfig;
StartLogDashBoard();
CheckForControlledSoftware();
Target target = LogManager.Configuration.FindTargetByName("RunFile");
if (target != null)
{
FileTarget fileTarget = null;
WrapperTargetBase wrapperTarget = target as WrapperTargetBase;
if (wrapperTarget == null)
{
fileTarget = target as FileTarget;
}
else
{
fileTarget = wrapperTarget.WrappedTarget as FileTarget;
}
// re-direct NLOG log to specific folder
fileTarget.FileName = FileAndFolderManager.GetFile(FileAndFolderManager.Files.NLOG_TEMP);
LogManager.ReconfigExistingLoggers();
}
UutInfo = new UutInfo(partNumber, serialNumber);
var configFolder = Path.Combine(FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.CONFIG), ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.INSTRUMENT_CONFIG_FOLDER_NAME.ToString()));
_instrumentManager = new GeneralInstrumentManager(assemblyFolder, configFolder, IsThereHardware);
_instrumentManager.Initialize();
MalMeasurementLibManager = new MalMeasurementLibManager(_instrumentManager);
_malMeasurementLibManager = new MalMeasurementLibManager(_instrumentManager);
}
catch (Exception)
{
@@ -186,24 +282,292 @@ namespace ProgramLib
{
_logger?.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() ...");
if (TestStandSeqContext != null)
if (TestStandSeqContext == null)
{
// clear any setting that we have added for any steps in the Setup Group
for (int i = 0; i < TestStandSeqContext.Sequence.GetNumSteps(StepGroups.StepGroup_Setup); i++)
// signal to all running threads to exit
EventManager[EventManager.Events.GLOBAL_QUIT].Set();
if (IsUutPwrOn)
{
TestStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Setup).PostExpression = "";
BasicAction action = new UutPowerOffAction();
action.Run();
}
// clear any setting that we have added for any steps in the Main Group
for (int i = 0; i < TestStandSeqContext.Sequence.GetNumSteps(StepGroups.StepGroup_Main); i++)
// needs to kill all the support threads since they could be accessing GUI
foreach (BasicThread thread in _threadList)
{
TestStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Main).PostExpression = "";
TestStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Main).AdditionalResults.CustomResults.Clear();
thread.Quit();
thread.WaitForExit();
}
}
}
private void KillSupportThreads()
/// <summary>
/// Perform initializations
/// </summary>
internal void Initialize()
{
UutInfo.Initialize();
EthernetSocketManager = new EthernetSocketManager();
TestStation = new TestStation();
TestStation.PerformCableChecks();
FileAndFolderManager.ConstructTestFolder(UutInfo, TestInfo);
RecordTestRunStartInfoToFile();
if (TestStandSeqContext != null)
{
string testStandReportPath = Path.Combine(FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST), ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.TESTSTAND_FOLDER_NAME.ToString()));
if (!Directory.Exists(testStandReportPath))
Directory.CreateDirectory(testStandReportPath);
string testStandVarName = ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.DESTINATION_TEST_REPORT_PATH_VAR_NAME.ToString());
TestStandSeqContext.Main.FileGlobals.SetValString(testStandVarName, PropertyOptions.PropOption_CaseInsensitive, testStandReportPath);
}
}
/// <summary>
/// Check if we are running controlled software
/// </summary>
internal void CheckForControlledSoftware()
{
try
{
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
IsAppControlled = false;
string drive = FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA).Substring(0, 2);
string appControlledPath = Path.Combine(drive, ProgramSpecificConfig.ReadValue(ProgramSpecificConfigIni.GENERAL.ToString(), ProgramSpecificConfigIni.APP_RELEASE_CONTROLLED_FOLDER.ToString()));
if (Regex.IsMatch(assemblyFolder, @"^" + Regex.Escape(appControlledPath), RegexOptions.IgnoreCase))
{
IsAppControlled = true;
if (TestStandSeqContext != null)
{
string testStandSeqFilePath = TestStandSeqContext.Sequence.SequenceFile.Path;
if (!Regex.IsMatch(testStandSeqFilePath, @"^" + Regex.Escape(appControlledPath), RegexOptions.IgnoreCase))
{
if (IsThereHardware)
_logger.Warn($"Uncontrolled TestStand sequence execution is detected. {testStandSeqFilePath} is not running out of {appControlledPath}.");
IsAppControlled = false;
}
}
}
else
{
if (IsThereHardware)
_logger.Warn($"Uncontrolled software execution is detected. {Assembly.GetExecutingAssembly().Location} is not running out of {appControlledPath}.");
}
}
catch { }
}
/// <summary>
/// Start LogDashboard application
/// </summary>
internal void StartLogDashBoard()
{
try
{
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string drive = FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA).Substring(0, 2);
string logDashboardAppPath = Path.Combine(drive, ProgramSpecificConfig.ReadValue(ProgramSpecificConfigIni.GENERAL.ToString(), ProgramSpecificConfigIni.LOG_DASHBOARD_APP_PATH.ToString()));
string logDashboardProcessName = Path.GetFileNameWithoutExtension(logDashboardAppPath);
if (!Path.IsPathRooted(logDashboardAppPath))
logDashboardAppPath = Path.Combine(assemblyFolder, logDashboardAppPath);
if (!String.IsNullOrEmpty(logDashboardAppPath) && !String.IsNullOrEmpty(logDashboardProcessName))
{
if (File.Exists(logDashboardAppPath))
{
Process[] procArray = Process.GetProcessesByName(logDashboardProcessName);
if (procArray.Length == 0)
{
Process.Start(logDashboardAppPath);
int maxRetries = 5;
int retriev = 0;
do
{
System.Threading.Thread.Sleep(1000);
procArray = Process.GetProcessesByName(logDashboardProcessName);
} while (retriev++ < maxRetries && procArray.Length == 0);
}
}
}
}
catch { }
}
/// <summary>
/// Perform post test activities
/// </summary>
internal void PerformPostTestActivities()
{
try
{
MoveNlogToTestFolder();
RecordTestRunEndInfoToFile();
CreatePdelFile();
}
catch (Exception) { }
}
/// <summary>
/// Create PDEL file if there are PDEL results
/// </summary>
internal void CreatePdelFile()
{
if (PcodeTestResultList.List.Count > 0)
{
PdelWriter pdelWriter = new PdelWriter();
string pdelReportPath = Path.Combine(FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST), ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.PDEL_FOLDER_NAME.ToString()));
pdelWriter.CreatePdelFile(PcodeTestResultList.List, PdelData, pdelReportPath);
}
}
/// <summary>
/// Move NLog to test folder
/// </summary>
private void MoveNlogToTestFolder()
{
if (FileAndFolderManager != null)
{
string nlogFilePath = FileAndFolderManager.GetFile(FileAndFolderManager.Files.NLOG_TEMP);
// move nlog log to test folder
if (File.Exists(nlogFilePath))
{
string destPath = String.Empty;
int numTries = 1;
while (numTries++ <= 2)
{
try
{
destPath = Path.Combine(FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST), ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.NLOG_FOLDER_NAME.ToString()));
}
catch (Exception)
{
FileAndFolderManager.ConstructTestFolder(UutInfo, TestInfo);
if (TestStandSeqContext != null)
{
string testStandReportPath = Path.Combine(FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST), ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.TESTSTAND_FOLDER_NAME.ToString()));
if (!Directory.Exists(testStandReportPath))
Directory.CreateDirectory(testStandReportPath);
string testStandVarName = ProgramGeneralConfig.ReadValue(ProgramGeneralConfigIni.GENERAL.ToString(), ProgramGeneralConfigIni.DESTINATION_TEST_REPORT_PATH_VAR_NAME.ToString());
TestStandSeqContext.Main.FileGlobals.SetValString(testStandVarName, PropertyOptions.PropOption_CaseInsensitive, testStandReportPath);
}
}
}
if (!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
if (Directory.Exists(destPath))
{
string destFilePath = Path.Combine(destPath, Path.GetFileName(nlogFilePath));
File.Move(nlogFilePath, destFilePath);
}
}
}
}
/// <summary>
/// Record general information about start of test run to file
/// </summary>
private void RecordTestRunStartInfoToFile()
{
XmlDocumentWrapper doc = new XmlDocumentWrapper(FileAndFolderManager.GetFile(FileAndFolderManager.Files.TEST_RUN_LOG));
XmlNodeList nodeList = doc.GetNodes(TestRunConfigXml.TestRunPath);
int maxNumTestRunRecordsToKeep = 50;
for (int i = maxNumTestRunRecordsToKeep - 1; i < nodeList.Count; i++)
{
doc.RemoveNode(nodeList[i]);
}
Dictionary<string, string> attributesDict = new Dictionary<string, string>();
attributesDict[TestRunConfigXml.TestRunPathAttributeName] = FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST);
attributesDict[TestRunConfigXml.TestRunTestNameAttributeName] = TestInfo.TestName;
attributesDict[TestRunConfigXml.TestRunTesterAttributeName] = TestInfo.TestOperator;
attributesDict[TestRunConfigXml.TestRunStartDateAttributeName] = TestInfo.TestStartDateTime.ToString("MM/dd/yyyy");
attributesDict[TestRunConfigXml.TestRunStartTimeAttributeName] = TestInfo.TestStartDateTime.ToString("HH:mm:ss");
doc.AddNode(TestRunConfigXml.TestRunPath, attributesDict, null, XmlDocumentWrapper.AddNodePosition.First);
doc.SaveToFile();
}
/// <summary>
/// Record general information about end of test run to file
/// </summary>
private void RecordTestRunEndInfoToFile()
{
XmlDocumentWrapper doc = new XmlDocumentWrapper(FileAndFolderManager.GetFile(FileAndFolderManager.Files.TEST_RUN_LOG));
XmlNodeList nodeList = doc.GetNodes(TestRunConfigXml.TestRunPath);
int maxNumTestRunRecordsToKeep = 50;
for (int i = maxNumTestRunRecordsToKeep - 1; i < nodeList.Count; i++)
{
doc.RemoveNode(nodeList[i]);
}
XmlNode node = doc.GetNode(TestRunConfigXml.TestRunPath);
if (node != null)
{
XmlAttribute attr = node.Attributes["path"];
if (attr != null)
{
if (String.Equals(attr.Value, FileAndFolderManager.GetFolder(FileAndFolderManager.Folders.DATA_TEST), StringComparison.OrdinalIgnoreCase))
{
Dictionary<string, string> attributesDict = new Dictionary<string, string>();
attributesDict[TestRunConfigXml.TestRunEndDateAttributeName] = DateTime.Now.ToString("MM/dd/yyyy");
attributesDict[TestRunConfigXml.TestRunEndTimeAttributeName] = DateTime.Now.ToString("HH:mm:ss");
doc.ChangeNode(node, attributesDict);
}
}
}
doc.SaveToFile();
}
/// <summary>
/// Kill GUI thread
/// </summary>
internal void KillGuiThread()
{
try
{
// Because the GuiManager class starts a STA thread to manage WPF GUIs
// the Program destructor doesn't get called if we don't shut down the STA thread first
// so we explicitly call Dispose here to shutdown the thread. This is a special case.
// All other classes call Dispose() in their Destructor
_program.GuiManager?.Dispose();
}
catch (MalMeasurementManagerNullReferenceException) { }
}
/// <summary>
/// Kill support threads
/// </summary>
internal void KillSupportThreads()
{
foreach (BasicThread thread in _threadList)
{
@@ -212,82 +576,18 @@ namespace ProgramLib
}
}
/// <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>
private void TerminateTestOnMainThreadError(Exception ex)
{
if (TestStandSeqContext != null)
{
_logger.Error(ex.Message + "\n" + ex.StackTrace);
lock (_terminateTestSyncObj)
{
if (!_terminateTestInitiated)
{
// tells teststand there's a exception occurred and give it the error message
TestStandSeqContext.Step.PostExpression = $"Step.Result.Error.Msg=\"{ex.Message} \", Step.Result.Error.Occurred=True";
TestStandSeqContext.Step.ResultStatus = "Error";
// tells TestStand to go to clean up
TestStandSeqContext.StepGroup = StepGroups.StepGroup_Cleanup;
TestStandSeqContext.NextStepIndex = 0;
_terminateTestInitiated = true;
}
}
}
else
throw ex;
}
/// <summary>
/// Support threads such as monitor threads need a way to stop the sequence if it encounters an error
/// This function is meant to be called from FailureMonitorThread which monitors any error signaled by any
/// support thread.
/// </summary>
/// <param name=""></param>
/// <returns></returns>
internal void TerminateTestOnSupportThreadError()
{
if (TestStandSeqContext != null)
{
lock (_terminateTestSyncObj)
{
if (!_terminateTestInitiated)
{
// tells teststand there's a exception occurred and give it the error message
TestStandSeqContext.SequenceErrorMessage = _fatalErrorMsgFromSupportThread + " ";
TestStandSeqContext.SequenceErrorOccurred = true;
// tells TestStand to go to clean up
TestStandSeqContext.StepGroup = StepGroups.StepGroup_Cleanup;
TestStandSeqContext.NextStepIndex = 0;
_terminateTestInitiated = true;
}
}
}
}
/// <summary>
/// Save error message originated from other threads
/// </summary>
/// <param name=""></param>
/// <returns></returns>
internal void SetFatalErrorMsgFromSupportThread(string errorMsg)
internal void SetFatalFailureExceptionFromSupportThread(Exception fatalFailureException)
{
lock(_fatalErrorMsgFromSupportThreadSyncObj)
lock (_fatalFailureExceptionFromSupportThreadSyncObj)
{
if (String.IsNullOrEmpty(_fatalErrorMsgFromSupportThread))
if (_fatalFailureExceptionFromSupportThread == null)
{
_fatalErrorMsgFromSupportThread = errorMsg;
_fatalFailureExceptionFromSupportThread = fatalFailureException;
}
}
}