Files
GenericTeProgramLibrary/Source/Program/Program.cs
2025-01-04 08:33:01 -07:00

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 _fatalErrorMsgFromThreadSyncObj = new object();
private string _fatalErrorMsgFromThread;
#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 = _fatalErrorMsgFromThread + " ";
_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 SetFatalErrorMsgFromThread(string errorMsg)
{
lock(_fatalErrorMsgFromThreadSyncObj)
{
if (String.IsNullOrEmpty(_fatalErrorMsgFromThread))
{
_fatalErrorMsgFromThread = errorMsg;
}
}
}
}
}