282 lines
11 KiB
C#
282 lines
11 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 NationalInstruments.TestStand.Interop.API;
|
|
using NLog;
|
|
using Raytheon.Instruments;
|
|
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Raytheon.Common;
|
|
|
|
namespace ProgramLib
|
|
{
|
|
/// <summary>
|
|
/// Class for interfacing with any Test Executive such as 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
|
|
{
|
|
#region PrivateMemberVariables
|
|
// the one and only program
|
|
private static Program _program;
|
|
|
|
private object _terminateTestSyncObj = new object();
|
|
private bool _terminateTestInitiated = false;
|
|
|
|
// simulation
|
|
private readonly bool _isThereHardware;
|
|
|
|
// file management
|
|
private string _partNumber;
|
|
private string _serialNumber;
|
|
|
|
private List<BasicThread> _threadList = new List<BasicThread>();
|
|
|
|
private IInstrumentManager _instrumentManager;
|
|
|
|
private ILogger _logger;
|
|
|
|
private object _fatalErrorMsgFromSupportThreadSyncObj = new object();
|
|
private string _fatalErrorMsgFromSupportThread;
|
|
|
|
#endregion
|
|
|
|
internal SequenceContext _testStandSeqContext;
|
|
internal UutInfo _uutInfo;
|
|
internal EventManager _eventManager = new EventManager();
|
|
internal FileAndFolderManager _fileAndFolderManager;
|
|
internal IConfigurationFile _programConfig { get; private set; }
|
|
internal bool _isUutPwrOn = false;
|
|
|
|
/// <summary>
|
|
/// Create an instance of this class. Only one instance is allowed
|
|
/// </summary>
|
|
/// <param name="partNumber">The UUT part number</param>
|
|
/// <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)
|
|
{
|
|
if (_program == null)
|
|
{
|
|
_program = new Program(partNumber, serialNumber, isThereHardware, testStandSeqContext);
|
|
}
|
|
|
|
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)
|
|
{
|
|
// 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));
|
|
|
|
_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;
|
|
|
|
// Initialze all other configuration that the program needs
|
|
_uutInfo = new UutInfo(_partNumber, _serialNumber);
|
|
|
|
try
|
|
{
|
|
var configFolder = Path.Combine(assemblyFolder, Raytheon.Common.GeneralConstants.InstrumentConfigFolder);
|
|
_instrumentManager = new GeneralInstrumentManager(assemblyFolder, configFolder, _isThereHardware);
|
|
_instrumentManager.Initialize();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalizer
|
|
/// </summary>
|
|
~Program()
|
|
{
|
|
_logger?.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() ...");
|
|
|
|
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++)
|
|
{
|
|
_testStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Setup).PostExpression = "";
|
|
}
|
|
|
|
// 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++)
|
|
{
|
|
_testStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Main).PostExpression = "";
|
|
_testStandSeqContext.Sequence.GetStep(i, StepGroups.StepGroup_Main).AdditionalResults.CustomResults.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void KillSupportThreads()
|
|
{
|
|
foreach (BasicThread thread in _threadList)
|
|
{
|
|
thread.Quit();
|
|
thread.WaitForExit();
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
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)
|
|
{
|
|
lock(_fatalErrorMsgFromSupportThreadSyncObj)
|
|
{
|
|
if (String.IsNullOrEmpty(_fatalErrorMsgFromSupportThread))
|
|
{
|
|
_fatalErrorMsgFromSupportThread = errorMsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|