// 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. -------------------------------------------------------------------------*/ // Ignore Spelling: Selftest ovp ocp using NLog; using Raytheon.Instruments; using Raytheon.Instruments.PowerSupply; using Raytheon.Common; using System; using System.CodeDom; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; namespace MeasurementManagerLib { /// /// The entry point for the library that controls power supplies. /// This class parses out the definition of all power supplies in a system and provides control to the host. /// In simple cases, the host only needs to fill in the ini file and use the OutputDisable() and OutputEnable() functions. /// A variety of other capability is exposed for more complex applications. /// public class PowerSupplyMeasurementManager : IDisposable { #region PublicClassMembers /// /// A callback definition for the power monitor. /// The host may pass in this delegate to PowerLogStart() to receive callbacks from the monitor thread. /// /// The callback returns a comma delimited string of the format: "System, Module, voltage, voltage setpoint, current, output status, fault status"; /// If an exception is thrown in the monitor thread, this value will be set to -1, else it will be 0; public delegate void PowerMonitorDelegate(List retData, int errorCode); #endregion #region PrivateClassMembers // power system name to power system object private SortedDictionary _powerSystemNameToObjectMap; // power module name to power system object private SortedDictionary _powerModuleNameToObjectMap; private PowerSupplyDataLogWorker _dataLogWorker; private Thread _dataLogThread; // these are used to remember the logging thread params if the host is loading new power def files string _prevPowerLogFileName; int _prevPowerLogRestTime; PowerMonitorDelegate _prevPowerLogCallback; private static NLog.ILogger _logger; private IInstrumentManager _instrumentManager; private string _powerSystemWithFailedSelfTest = String.Empty; #endregion #region PublicFuctions /// /// will create an array of power systems with power supplies /// /// /// public PowerSupplyMeasurementManager(IInstrumentManager instrumentManager) { _logger = LogManager.GetCurrentClassLogger(); _instrumentManager = instrumentManager; _powerSystemNameToObjectMap = new SortedDictionary(); // create some maps to support the functions the MAL input _powerSystemNameToObjectMap = new SortedDictionary(StringComparer.InvariantCultureIgnoreCase); _powerModuleNameToObjectMap = new SortedDictionary(StringComparer.InvariantCultureIgnoreCase); ICollection powerSystemList = _instrumentManager.GetInstruments(typeof(IPowerSupplySystem)); // populate the maps foreach (IPowerSupplySystem powerSystem in powerSystemList) { powerSystem.Initialize(); _powerSystemNameToObjectMap[powerSystem.Name.ToUpper()] = powerSystem; List moduleNames = powerSystem.GetModuleNames(); foreach (string moduleName in moduleNames) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper())) { throw new Exception("There is more than 1 power system that have the same module name: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()] = powerSystem; } } PerformPowerSupplySelfTests(); if (_powerSystemWithFailedSelfTest != String.Empty) { throw new Exception($"{_powerSystemWithFailedSelfTest}'s self-test failed."); } } /// /// Perform self test on power supply system /// Self test for each power system takes a while, so we don't want to run self test every time we initialize the power system /// So we only want to run self test under 2 conditions: /// 1. Certain time has elapsed since last power off /// 2. Certain time has elapsed since last self test run ( in the absence of power off time) /// /// /// private void PerformPowerSupplySelfTests() { _logger.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() method..."); string errorMsg = String.Empty; ICollection powerSystemList = _instrumentManager.GetInstruments(typeof(IPowerSupplySystem)); Task[] taskArray = new Task[powerSystemList.Count]; int index = 0; foreach (IPowerSupplySystem powerSystem in powerSystemList) { // perform self test on power system Task task = Task.Factory.StartNew(() => PerformPowerSupplySelfTestTask(powerSystem)); taskArray.SetValue(task,index++); } Task.WaitAll(taskArray); } /// /// Perform self test on power supply system /// /// /// private void PerformPowerSupplySelfTestTask(IPowerSupplySystem powerSystem) { SelfTestResult result = SelfTestResult.Pass; _logger?.Debug($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() for {powerSystem.Name} is running..."); try { result = powerSystem.PerformSelfTest(); if (result == SelfTestResult.Fail && String.IsNullOrEmpty(_powerSystemWithFailedSelfTest)) { _powerSystemWithFailedSelfTest = powerSystem.Name; } } catch (Exception ex) { _logger?.Error(ex.Message + "\n" + ex.StackTrace); } _logger?.Debug($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() for {powerSystem.Name} is exiting..."); } /// /// Disable the power supply display interface. /// /// The name of the system to disable, as defined in the config file public void DisplayDisable(string powerSystem) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } _powerSystemNameToObjectMap[powerSystem.ToUpper()].DisplayEnabled = false; } /// /// Enable the power supply display interface. /// /// The name of the system to disable, as defined in the config file public void DisplayEnable(string powerSystem) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } _powerSystemNameToObjectMap[powerSystem.ToUpper()].DisplayEnabled = true; } /// /// Dispose of this object. /// public void Dispose() { try { Dispose(true); GC.SuppressFinalize(this); } catch (Exception err) { try { ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace); } catch (Exception) { //Do not rethrow. Exception from error logger that has already been garbage collected } } } /// /// Get all of the power module names /// /// Array of the system/module names. public List GetPowerModuleList() { List powerModules = new List(); foreach (KeyValuePair item in _powerModuleNameToObjectMap) { powerModules.Add(item.Key); } return powerModules; } /// /// Get the power module names for a given system /// /// Array of the system/module names. public List GetPowerSupplyList(string powerSystem) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Unknown power system: " + powerSystem); } return _powerSystemNameToObjectMap[powerSystem.ToUpper()].GetModuleNames(); }//GetPowerSupplyModuleInfoDict /// /// Get the dictionary that contains configuration information for each module /// /// public Dictionary GetPowerSupplyModuleInfoDict(string powerSystem) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Unknown power system: " + powerSystem); } return _powerSystemNameToObjectMap[powerSystem.ToUpper()].GetPowerSupplyModuleInfoDict(powerSystem); } /// /// Gets the Power System names. /// /// Array of power system names, as defined in the config file public List GetPowerSupplySystemList() { List powerSystems = new List(); foreach (KeyValuePair item in _powerSystemNameToObjectMap) { powerSystems.Add(item.Key); } return powerSystems; } /// /// Query the supply and returns the programed OCP. Should match what was in the definition file. /// /// The name of the power supply. /// The programmed overcurrent protection value public double GetOverCurrentProtection(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].GetOverCurrentSetting(moduleName); } /// /// Query the supply and returns the programed OVP. Should match what was in the definition file. /// /// The name of the power supply. /// The programmed overvoltage protection value. public double GetOverVoltageProtection(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].GetOverVoltageSetting(moduleName); } /// /// Query the supply and returns the programed slew rate /// /// /// The programmed slew rate public double GetSlewRate(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].GetSlewRate(moduleName); } /// /// Query the system that contains the passed in module and returns the system error code. /// /// The module to query. /// The error code. /// The string form of the error code. Will be empty string if there is no error. public string GetSystemErrorCode(string moduleName, out int errorCode) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].GetErrorCode(out errorCode); } /// /// Query the supply and returns the programmed voltage setpoint. /// After construction, this should match what was in the definition file. /// The host may override the value by calling SetVoltageSetpoint(). /// /// The name of the power supply. /// The programmed voltage setpoint. public double GetVoltageSetpoint(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].GetVoltageSetting(moduleName); } /// /// Query the supply and returns true if output is enabled. /// /// The name of the power supply. /// True if output is enabled, false if it is not enabled. public bool IsPowerSupplyOn(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].IsOutputOn(moduleName); } /// /// Sends a SCPI command to the power system and reads the response /// /// The name of the power system. /// The SCPI command to send /// public string IOQuery(string powerSystem, string command) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } return _powerSystemNameToObjectMap[powerSystem.ToUpper()].IOQuery(command); } /// /// Sends a SCPI command to the power system /// /// The name of the power system. /// The SCPI command to send public void IOWrite(string powerSystem, string command) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } _powerSystemNameToObjectMap[powerSystem.ToUpper()].IOWrite(command); } /// /// Read the current of a module. /// /// The module to read. /// The current (Amps). public double MeasureCurrent(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].MeasureCurrent(moduleName); } /// /// Query the supply for its voltage. /// /// The module to read. /// The voltage (Volts). public double MeasureVoltage(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].MeasureVoltage(moduleName); } /// /// Disable the output of the power supply. /// /// The name of the power supply module. public void OutputDisable(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].Off(moduleName); } /// /// Enable the output of the power supply. /// /// The name of the module to enable. public void OutputEnable(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].On(moduleName); } /// /// start logging of power supply data. /// Queries the list of all power modules. /// Logs data to the define path. /// Returns data to defined callback function. /// /// Path to create the log file. /// The time to wait between queries. /// Function to call. Null can be passed in in which case no hot callback will be issued public void PowerLogStart(string fileName, int threadRestTimeMs, PowerMonitorDelegate callback) { // if we have been running before, stop just in case the host did not if (_dataLogWorker != null) { PowerLogStop(); _dataLogWorker.Dispose(); } _dataLogWorker = new PowerSupplyDataLogWorker(this, fileName, threadRestTimeMs, callback); _dataLogThread = new Thread(_dataLogWorker.DoWork); // start the thread back up _dataLogThread.Start(); _prevPowerLogFileName = fileName; _prevPowerLogRestTime = threadRestTimeMs; _prevPowerLogCallback = callback; } /// /// stops logging data and closes the file. /// public void PowerLogStop() { // Wait up to 5 seconds for the thread to quit const int THREAD_QUIT_TIMEOUT_MS = 5000; string functionName = $"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}()"; if (_dataLogWorker != null) { _dataLogWorker.QuitWork(); if ((_dataLogThread != null) && _dataLogThread.IsAlive) { bool didThreadQuit = _dataLogThread.Join(THREAD_QUIT_TIMEOUT_MS); if (didThreadQuit == false) { _logger.Error($"{functionName} - Logging Thread did not quit as expected, aborting it"); _dataLogThread.Abort(); } else { _logger.Debug("{functionName} - Logging Thread quit successfully after join"); } } else { _logger.Debug("{functionName} - Logging Thread quit successfully"); } } _prevPowerLogFileName = null; _prevPowerLogRestTime = 0; _prevPowerLogCallback = null; } /// /// Control the power supply internal mechanical relay state /// /// True to connect, false to disconnect public void MechanicalRelayOutputControl(string moduleName, bool shallWeConnect) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].MechanicalRelayOutputControl(moduleName, shallWeConnect); } /// /// Query all power supply data in a single call. /// /// The name of the module to query. /// The voltage that was read. /// The voltage setpoint that was read. /// The current that was read. /// The output status. True if output is enabled, false if output is disabled. /// The value of the fault status register. See power supply docs for meaning (0 means no fault). /*public void ReadPowerData(string moduleName, out double voltage, out double voltageSetpoint, out double current, out bool outputStatus, out int faultStatus) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("PowerSupplyManager::ReadPowerData() - could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].ReadPowerData(moduleName, out voltage, out voltageSetpoint, out current, out outputStatus, out faultStatus); }*/ /// /// Query all power supply data in a single call. /// /// The name of the module to query. public PowerData ReadPowerData(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].ReadPowerData(moduleName); } /// /// /// /// /// public int ReadProtectionStatus(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } return _powerModuleNameToObjectMap[moduleName.ToUpper()].ReadProtectionStatus(moduleName); } /// /// Resets the setpoint voltage to the value contained in the ini file /// /// The module to reset public void SetInitialVoltage(string moduleName) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].SetInitialVoltage(moduleName); } /// /// Every power supply gets its voltage set to what was in the ini file /// public void SetInitialVoltageAll() { List powerModules = GetPowerModuleList(); foreach (string powerModule in powerModules) { SetInitialVoltage(powerModule); } } /// /// Sets the slew rate in volts per second /// /// The name of the power module /// slew in volts per second /*public void SetSlewRate(string moduleName, double slewRate) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("PowerSupplyManager::SetSlewRate() - could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].SetSlewRate(moduleName, slewRate); }*/ /// /// /// /// /// public void SetOverCurrentProtection(string moduleName, double ocpValue) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].SetOverCurrentProtection(moduleName, ocpValue); } /// /// /// /// /// public void SetOverVoltageProtection(string moduleName, double ovpValue) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].SetOverVoltageProtection(moduleName, ovpValue); } /// /// Set the setpoint voltage of a module. /// /// The module to set. /// The setpoint voltage. public void SetVoltageSetpoint(string moduleName, double setpointVoltage) { if (_powerModuleNameToObjectMap.ContainsKey(moduleName.ToUpper()) == false) { throw new Exception("Could not find module: " + moduleName); } _powerModuleNameToObjectMap[moduleName.ToUpper()].SetVoltageSetpoint(moduleName, setpointVoltage); } /// /// Disable the power system watchdog. /// /// The power system to act on. public void WatchdogDisable(string powerSystem) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } _powerSystemNameToObjectMap[powerSystem.ToUpper()].WatchdogDisable(); } /// /// Enable the power system watchdog. /// /// The system to act on. /// The number of seconds for the watchdog. public void WatchdogEnable(string powerSystem, uint timeInSeconds) { if (_powerSystemNameToObjectMap.ContainsKey(powerSystem.ToUpper()) == false) { throw new Exception("Could not find power system: " + powerSystem); } _powerSystemNameToObjectMap[powerSystem.ToUpper()].WatchdogEnable(timeInSeconds); } #endregion #region PrivateClassFunctions /// /// The Finalizer /// ~PowerSupplyMeasurementManager() { Dispose(false); } /// /// Dispose of this object. /// /// True = currently disposing, False = not disposing. protected virtual void Dispose(bool disposing) { if (disposing) { _logger.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() method..."); // stop the logging if it is still running try { PowerLogStop(); } catch (Exception ex) { _logger.Error(ex.Message + "\r\n" + ex.StackTrace); } // dispose the thread try { if (_dataLogWorker != null) { _dataLogWorker.Dispose(); } } catch (Exception ex) { _logger.Error(ex.Message + "\r\n" + ex.StackTrace); } // dispose of the other resources foreach (KeyValuePair entry in _powerSystemNameToObjectMap) { try { entry.Value.Shutdown(); } catch (Exception ex) { _logger.Error(ex.Message + "\r\n" + ex.StackTrace); } } } } #endregion } }