diff --git a/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67XX.csproj b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67XX.csproj
new file mode 100644
index 0000000..e832d16
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67XX.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net472
+ Library
+ Raytheon.Instruments.PowerSupplies.Keysight67XX
+ Keysight 67XX Series Power Supply
+ Keysight 67XX Series Power Supply
+
+
+
+
+
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerFactory.cs b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerFactory.cs
new file mode 100644
index 0000000..f5cbe95
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerFactory.cs
@@ -0,0 +1,117 @@
+/*-------------------------------------------------------------------------
+// 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 NLog;
+using Raytheon.Common;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Reflection;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ [ExportInstrumentFactory(ModelNumber = "PowerSupplyKeysightN67xxFactory")]
+ public class KeysightN67xxPowerFactory : IInstrumentFactory
+ {
+ ///
+ /// The supported interfaces
+ ///
+ private readonly List _supportedInterfaces = new List();
+ private static ILogger _logger;
+ private readonly IConfigurationManager _configurationManager;
+ private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
+ private static string DefaultPath;
+
+ public KeysightN67xxPowerFactory(string defaultConfigPath = DefaultConfigPath)
+ : this(null, defaultConfigPath)
+ {
+ }
+
+ ///
+ /// COECommDeviceInstrumentFactory injection constructor
+ ///
+ ///
+ ///
+ ///
+ [ImportingConstructor]
+ public KeysightN67xxPowerFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
+ [Import(AllowDefault = true)] string defaultConfigPath = null)
+ {
+ DefaultPath = defaultConfigPath;
+ _logger = LogManager.GetCurrentClassLogger();
+
+ if (NLog.LogManager.Configuration == null)
+ {
+ var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
+ }
+
+ _configurationManager = configManager ?? GetConfigurationManager();
+ _supportedInterfaces.Add(typeof(PowerSupply));
+ }
+
+ ///
+ /// Gets the instrument
+ ///
+ ///
+ ///
+ public IInstrument GetInstrument(string name)
+ {
+ return null;
+ }
+
+ ///
+ /// Gets the instrument
+ ///
+ ///
+ ///
+ public object GetInstrument(string name, bool simulateHw)
+ {
+ try
+ {
+ if (simulateHw)
+ return new PowerSupplySim(name, _configurationManager, _logger);
+ else
+ return new KeysightN67xxPowerSupply(name, _configurationManager, _logger);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, $"Unable to construct {name} instrument instance");
+ return null;
+ }
+ }
+
+ ///
+ /// Gets supported interfaces
+ ///
+ ///
+ public ICollection GetSupportedInterfaces()
+ {
+ return _supportedInterfaces.ToArray();
+ }
+
+ ///
+ /// returns confiuration based on the predefined path or default path c:/ProgramData/Raytheon/InstrumentManagerService
+ ///
+ ///
+ private static IConfigurationManager GetConfigurationManager()
+ {
+ return string.IsNullOrEmpty(DefaultPath) ? new RaytheonConfigurationManager() : new RaytheonConfigurationManager(DefaultPath);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerModule.cs b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerModule.cs
new file mode 100644
index 0000000..c9408dd
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerModule.cs
@@ -0,0 +1,662 @@
+/*-------------------------------------------------------------------------
+// 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 NLog;
+using Raytheon.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ ///
+ /// Class to simulate any power supply module
+ ///
+ class KeysightN67xxPowerModule : PowerSupplyModule
+ {
+ private List _groupedModules;
+ private List _coupledModules;
+ private string _powerSupplySystemName;
+ private EthernetSockets.TcpClient _tcpClient;
+ IConfigurationFile _config;
+
+ private ILogger _logger;
+
+ ///
+ /// Constructor
+ ///
+ public KeysightN67xxPowerModule(string iniFilePath, string powerSupplySystemName)
+ {
+ _logger = LogManager.GetCurrentClassLogger();
+ _powerSupplySystemName = powerSupplySystemName;
+
+ _config = new ConfigurationFile(iniFilePath);
+
+ string ipAddress = _config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.ETHERNET_ADDRESS.ToString());
+ int port = 0;
+ Int32.TryParse(_config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.ETHERNET_PORT.ToString()), out port);
+ string moduleDef = _config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.MODULE_DEFINITION.ToString());
+ string coupledModules = _config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.COUPLED_MODULES.ToString());
+ string groupedModules = _config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.GROUPED_MODULES.ToString());
+
+ PowerModules = moduleDef.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ _coupledModules = coupledModules.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ _groupedModules = groupedModules.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+
+ _tcpClient = new EthernetSockets.TcpClient(ipAddress, port);
+ _tcpClient.Initialize();
+ _tcpClient.Connect();
+
+ ResetPowerSupplySystem();
+
+ bool systemRebooted = false;
+ if (_groupedModules.Count() > 1)
+ GroupModules(_groupedModules, out systemRebooted);
+ else if (_coupledModules.Count() > 1)
+ CoupleModules(_coupledModules);
+
+ if (systemRebooted)
+ {
+ _tcpClient.Disconnect();
+ _tcpClient.Connect();
+ }
+
+ if (_groupedModules.Count() > 1)
+ {
+ PowerModules.Clear();
+ // since modules are grouped, we pick the first module as the representative module
+ PowerModules.Add(_groupedModules[0]);
+ }
+
+ // build the power module map
+ string moduleIndex = "";
+ double ovp = 0.0;
+ double ocp = 0.0;
+ double voltageSetPoint = 0.0;
+ double voltageSlewRate = -1.0;
+
+ double minVoltage = 0.0;
+ double maxVoltage = 0.0;
+ double minCurrent = 0.0;
+ double maxCurrent = 0.0;
+
+ for (int i = 0; i < PowerModules.Count(); i++)
+ {
+ string moduleName = PowerModules[i];
+
+ moduleIndex = _config.ReadValue(moduleName, PowerSupplyConfigIni.INDEX.ToString());
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.OCP.ToString()), out ocp);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.OVP.ToString()), out ovp);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.VOLTAGE_SETPOINT.ToString()), out voltageSetPoint);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.VOLTAGE_SLEW_RATE.ToString()), out voltageSlewRate);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.MIN_VOLTAGE.ToString()), out minVoltage);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.MAX_VOLTAGE.ToString()), out maxVoltage);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.MIN_CURRENT.ToString()), out minCurrent);
+ Double.TryParse(_config.ReadValue(moduleName, PowerSupplyConfigIni.MAX_CURRENT.ToString()), out maxCurrent);
+
+ PowerModuleInfoDict[moduleName] = new Raytheon.Instruments.PowerSupplies.PowerSupplyModuleInfo(moduleIndex, ocp, ovp, voltageSetPoint, voltageSlewRate, minVoltage, maxVoltage, minCurrent, maxCurrent);
+
+ ActivePowerModule = moduleName;
+ SetConstantVoltageMode();
+ SetAndConfirmOvp();
+ SetAndConfirmOcp();
+ SetAndConfirmVoltageSetpoint();
+ SetAndConfirmVoltageSlewRate();
+ }
+ }
+
+ ///
+ /// Enable or Disable Front Panel
+ ///
+ ///
+ ///
+ public override bool DisplayEnabled
+ {
+ set
+ {
+ SemObj?.Release();
+ }
+ }
+
+ ///
+ /// Enable or disable Front Panel
+ ///
+ ///
+ ///
+ public override bool FrontPanelEnabled
+ {
+ set
+ {
+ string command;
+
+ try
+ {
+ lock (SyncObj)
+ {
+ if (value)
+ command = KeysightPowerSupplyScpiCommands.SET_FRONTPANEL_ENABLE_CMD + "\n";
+ else
+ command = KeysightPowerSupplyScpiCommands.SET_FRONTPANEL_DISABLE_CMD + "\n";
+
+ SendCommand(command);
+ }
+ }
+ finally { SemObj?.Release(); }
+ }
+ }
+
+ ///
+ /// Turn on power module's output
+ ///
+ ///
+ ///
+ public override void On()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ string command = KeysightPowerSupplyScpiCommands.SET_OUTPUT_ENABLE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+ }
+ }
+ finally { SemObj?.Release(); }
+ }
+
+ ///
+ /// Turn off power module's output
+ ///
+ ///
+ ///
+ public override void Off()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ string command = KeysightPowerSupplyScpiCommands.SET_OUTPUT_DISABLE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+ }
+ }
+ finally { SemObj?.Release(); }
+ }
+
+ ///
+ /// Perform self test
+ ///
+ ///
+ ///
+ public override SelfTestResult PerformSelfTest()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SELFTEST_CMD + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ string[] stringVec = rsp.Split(',');
+
+ int status = -1;
+ Int32.TryParse(stringVec[0], out status);
+
+ if (status != 0)
+ {
+ SelfTestResult = SelfTestResult.Fail;
+ throw new Exception($"{_powerSupplySystemName}'s self-test failed with error code: {status}");
+ }
+ else
+ SelfTestResult = SelfTestResult.Pass;
+ }
+ }
+ finally { SemObj?.Release(); }
+
+ return SelfTestResult;
+ }
+
+ ///
+ /// Set constant voltage mode
+ ///
+ ///
+ ///
+ private void SetConstantVoltageMode()
+ {
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SET_CONSTANT_VOLTAGE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+ }
+ }
+
+ ///
+ /// Set and Confirm OVP
+ ///
+ ///
+ ///
+ private void SetAndConfirmOvp()
+ {
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SET_OVP_CMD + " " + PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_ + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+
+ command = KeysightPowerSupplyScpiCommands.READ_OVP_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+ double ovp = 0.0;
+
+ Double.TryParse(rsp, out ovp);
+
+ if (ovp != PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_)
+ throw new Exception($"Unable to set OVP. Expected OVP: {PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_}. Actual: {ovp}");
+ }
+ }
+
+ ///
+ /// Set and Confirm OCP
+ ///
+ ///
+ ///
+ private void SetAndConfirmOcp()
+ {
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SET_OCP_CMD + " " + PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_ + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+
+ command = KeysightPowerSupplyScpiCommands.READ_OCP_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+ double ocp = 0.0;
+
+ Double.TryParse(rsp, out ocp);
+
+ if (ocp != PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_)
+ throw new Exception($"Unable to set OCP. Expected OCP: {PowerModuleInfoDict[ActivePowerModule].overVoltageProtection_}. Actual: {ocp}");
+ }
+ }
+
+ ///
+ /// Set and Confirm Voltage Setpoint
+ ///
+ ///
+ ///
+ private void SetAndConfirmVoltageSetpoint()
+ {
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SET_VOLTAGE_SETPOINT_CMD + " " + PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_ + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+
+ command = KeysightPowerSupplyScpiCommands.READ_VOLTAGE_SETPOINT_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+ double voltage = 0.0;
+
+ Double.TryParse(rsp, out voltage);
+
+ if (voltage != PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_)
+ throw new Exception($"Unable to set Voltage Setpoint. Expected Voltage Setpoint: {PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_}. Actual: {voltage}");
+ }
+ }
+
+ ///
+ /// Set and Confirm Slew Rate
+ ///
+ ///
+ ///
+ private void SetAndConfirmVoltageSlewRate()
+ {
+ lock (SyncObj)
+ {
+ if (PowerModuleInfoDict[ActivePowerModule].voltageSlewRate_ > 0.0)
+ {
+ string command = KeysightPowerSupplyScpiCommands.SET_VOLTAGE_SLEW_RATE_CMD + " " + PowerModuleInfoDict[ActivePowerModule].voltageSlewRate_ + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ SendCommand(command);
+
+ command = KeysightPowerSupplyScpiCommands.READ_VOLTAGE_SLEW_RATE_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+ double voltageSlewRate = 0.0;
+
+ Double.TryParse(rsp, out voltageSlewRate);
+
+ if (voltageSlewRate != PowerModuleInfoDict[ActivePowerModule].voltageSlewRate_)
+ throw new Exception($"Unable to set Voltage Slew Rate. Expected Voltage Slew Rate: {PowerModuleInfoDict[ActivePowerModule].voltageSlewRate_}. Actual: {voltageSlewRate}");
+ }
+ }
+ }
+
+ ///
+ /// Get error code
+ ///
+ ///
+ ///
+ protected override string GetErrorCode(out int errorCode)
+ {
+ errorCode = 0;
+ string rtn = "";
+
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.READ_ERROR_CODE_CMD + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ string[] stringVec = rsp.Split(',');
+
+ Int32.TryParse(stringVec[0], out errorCode);
+
+ if (stringVec.Length > 1)
+ {
+ rtn = stringVec[1];
+ }
+ }
+
+
+ return rtn;
+ }
+
+ ///
+ /// Read voltage
+ ///
+ ///
+ ///
+ protected override double ReadVoltage()
+ {
+ double val = 0.0;
+
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.READ_VOLTAGE_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ Double.TryParse(rsp, out val);
+ }
+
+ return val;
+ }
+
+ ///
+ /// Read current
+ ///
+ ///
+ ///
+ protected override double ReadCurrent()
+ {
+ double val = 0.0;
+
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.READ_CURRENT_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ Double.TryParse(rsp, out val);
+ }
+
+ return val;
+
+ }
+
+ ///
+ /// Read protection status
+ ///
+ ///
+ ///
+ protected override int ReadProtectionStatus()
+ {
+ int val = -1;
+
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.READ_PROTECTION_STATUS_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ Int32.TryParse(rsp, out val);
+ }
+
+ return val;
+ }
+
+ ///
+ /// Determine if module's output is on
+ ///
+ ///
+ ///
+ protected override bool IsOutputOn()
+ {
+ bool isOn = false;
+
+ lock (SyncObj)
+ {
+ string command = KeysightPowerSupplyScpiCommands.READ_OUTPUT_STATUS_CMD + " " + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
+
+ string rsp = SendCommandAndGetResponse(command);
+
+ int status = -1;
+
+ Int32.TryParse(rsp, out status);
+
+ if (status == 1)
+ {
+ isOn = true;
+ }
+ }
+
+ return isOn;
+ }
+
+ ///
+ /// When modules are grouped, they act as one module
+ ///
+ ///
+ ///
+ private void GroupModules(List moduleList, out bool systemRebooted)
+ {
+ // 1. Group the channels
+ string groupListToDefine = "(@";
+ string groupListToQuery = "";
+ string moduleNumber = "";
+
+ systemRebooted = false;
+
+ for (int i = 0; i < moduleList.Count; i++)
+ {
+ moduleNumber = _config.ReadValue(moduleList[i], PowerSupplyConfigIni.INDEX.ToString());
+
+ groupListToDefine += moduleNumber;
+ groupListToQuery += moduleNumber;
+
+ // add a ',' if this is not the final element in the list
+ if (i < moduleList.Count() - 1)
+ {
+ groupListToDefine += ",";
+ groupListToQuery += ",";
+ }
+ else
+ {
+ groupListToDefine += ")";
+ }
+ }
+
+ groupListToQuery = "\"" + groupListToQuery + "\"\n";
+
+ // see if channels are grouped
+ string queryGroupCommand = KeysightPowerSupplyScpiCommands.QUERY_GROUP_CHANNELS + "\n";
+
+ for (int i = 1; i <= 2; i++)
+ {
+ string respStr = SendCommandAndGetResponse(queryGroupCommand);
+
+ // if modules are not grouped
+ if (respStr != groupListToQuery)
+ {
+ groupListToDefine += "\n";
+
+ string groupCommand = KeysightPowerSupplyScpiCommands.SET_GROUP_DEFINE_CMD + " " + groupListToDefine;
+
+ SendCommand(groupCommand);
+ }
+ else if (i == 1)
+ {
+ break;
+ }
+ else
+ {
+ string command = KeysightPowerSupplyScpiCommands.REBOOT_CMD + "\n";
+ // after grouping the modules, need to reboot system for it to take effect
+ SendCommand(command);
+
+ // wait 20 seconds for reboot
+ Thread.Sleep(20000);
+
+ systemRebooted = true;
+ }
+ }
+ }
+
+ ///
+ /// When modules are coupled, they turned on and off in unison
+ /// i.e turn on/off one module, all other coupled modules will automatically turn on/off
+ ///
+ ///
+ ///
+ private void CoupleModules(List moduleList)
+ {
+ string coupleListToDefine = "";
+ string moduleNumber = "";
+
+ for (int i = 0; i < moduleList.Count(); i++)
+ {
+ moduleNumber = _config.ReadValue(moduleList[i], PowerSupplyConfigIni.INDEX.ToString());
+
+ coupleListToDefine += moduleNumber;
+
+ // add a ',' if this is not the final element in the list
+ if (i < moduleList.Count() - 1)
+ {
+ coupleListToDefine += ",";
+ }
+ }
+
+ coupleListToDefine += "\n";
+
+ // see if channels are grouped
+ string queryCoupleChannelCommand = KeysightPowerSupplyScpiCommands.QUERY_COUPLE_CHANNELS + "\n";
+
+ for (int i = 1; i <= 2; i++)
+ {
+ string respStr = SendCommandAndGetResponse(queryCoupleChannelCommand);
+
+ string queryCoupleStateCommand = KeysightPowerSupplyScpiCommands.QUERY_COUPLE_STATE + "\n";
+ string respStr2 = SendCommandAndGetResponse(queryCoupleStateCommand);
+
+ if (coupleListToDefine != respStr || respStr2 == "0\n")
+ {
+ // send command to couple modules
+ string command = KeysightPowerSupplyScpiCommands.SET_COUPLE_CHANNELS_CMD + " " + coupleListToDefine;
+ SendCommand(command);
+
+ // turn coupling on
+ command = KeysightPowerSupplyScpiCommands.SET_COUPLE_ON_CMD + "\n";
+ SendCommand(command);
+
+ // output protection on
+ command = KeysightPowerSupplyScpiCommands.SET_COUPLE_OUTPUT_PROTECT_ON_CMD + "\n";
+ SendCommand(command);
+ }
+ else if (i == 1)
+ break;
+ }
+ }
+
+ ///
+ /// Reset Power Supply System
+ ///
+ ///
+ ///
+ private void ResetPowerSupplySystem()
+ {
+ // send the command
+ string command = KeysightPowerSupplyScpiCommands.RESET_CMD + "\n";
+ SendCommand(command);
+ }
+
+ ///
+ /// Send command
+ ///
+ ///
+ ///
+ private void SendCommand(string command)
+ {
+ lock (SyncObj)
+ {
+ byte[] msg = Encoding.ASCII.GetBytes(command);
+
+ _tcpClient.Write(msg, (uint)msg.Length);
+ }
+ }
+
+ ///
+ /// Send command and get response
+ ///
+ ///
+ ///
+ private string SendCommandAndGetResponse(string command)
+ {
+ lock (SyncObj)
+ {
+ byte[] readBuf = new byte[100];
+ _tcpClient.SetReadTimeout(2000);
+
+ int bytesRec = 0;
+
+ SendCommand(command);
+
+ try
+ {
+ bytesRec = (int)_tcpClient.Read(ref readBuf);
+ }
+ catch (SocketException ex)
+ {
+ throw new Exception("SocketException Error Code: " + ex.ErrorCode + $" ({((SocketError)ex.ErrorCode).ToString()})");
+ }
+
+ return Encoding.ASCII.GetString(readBuf, 0, bytesRec);
+ }
+ }
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerSupply.cs b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerSupply.cs
new file mode 100644
index 0000000..1c03014
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightN67xxPowerSupply.cs
@@ -0,0 +1,121 @@
+// 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.IO;
+using System.Reflection;
+using NLog;
+using Raytheon.Common;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ ///
+ /// Class for controlling a Keysightt N6700 Series Power Supply System
+ ///
+ public class KeysightN67xxPowerSupply : PowerSupply
+ {
+ private string _iniFilePath;
+
+ private static ILogger _logger;
+ private readonly IConfigurationManager _configurationManager;
+ private readonly IConfiguration _configuration;
+
+ ///
+ /// Constructor
+ ///
+ public KeysightN67xxPowerSupply(string deviceInstanceName, IConfigurationManager configurationManager, ILogger logger)
+ {
+ Name = deviceInstanceName;
+ _logger = logger;
+ _configurationManager = configurationManager;
+
+ // configuration obtained from [deviceInstanceName].xml file
+ _configuration = _configurationManager.GetConfiguration(Name);
+
+ string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ _iniFilePath = Path.Combine(assemblyFolder, _configuration.GetConfigurationValue(deviceInstanceName, PowerSupplyConfigXml.INI_FILE_PATH.ToString()));
+ }
+
+ ///
+ /// Perform shutdown
+ ///
+ ///
+ ///
+ public override void Shutdown()
+ {
+ }
+
+ ///
+ /// Perform reset
+ ///
+ ///
+ ///
+ public override void Reset()
+ {
+ }
+
+ ///
+ /// Clear errors
+ ///
+ ///
+ ///
+ public override bool ClearErrors()
+ {
+ return true;
+ }
+
+ ///
+ /// Implement Indexer in order to access a specific power module
+ ///
+ ///
+ ///
+ public override PowerSupplyModule this[object powerDeviceId]
+ {
+ get
+ {
+ string powerDeviceName = String.Empty;
+
+ if (powerDeviceId != null && (powerDeviceId.GetType().IsEnum || powerDeviceId is string))
+ {
+ powerDeviceName = powerDeviceId.ToString();
+ }
+ else if (powerDeviceId != null)
+ {
+ throw new ArgumentException($"{nameof(powerDeviceId)} must be null or enumerated or string type");
+ }
+
+ _powerSupplyModule.GetSemphamore().WaitOne();
+
+ if (powerDeviceId != null)
+ _powerSupplyModule.SetActivePowerModule(powerDeviceName);
+
+ return _powerSupplyModule;
+ }
+ }
+
+ ///
+ /// Group or couple modules as specified in config file
+ /// Gather information for each power module from config file
+ ///
+ ///
+ ///
+ public override void Initialize()
+ {
+ _powerSupplyModule = new KeysightN67xxPowerModule(_iniFilePath, Name);
+ }
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/Keysight67XX/KeysightPowerSupplyScpiCommands.cs b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightPowerSupplyScpiCommands.cs
new file mode 100644
index 0000000..12694dd
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/Keysight67XX/KeysightPowerSupplyScpiCommands.cs
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ public static class KeysightPowerSupplyScpiCommands
+ {
+ public static string CLEAR_CMD = "*CLS";
+ public static string RESET_CMD = "*RST";
+ public static string SELFTEST_CMD = "*TST?";
+ public static string READ_ERROR_CODE_CMD = "SYST:ERR?";
+ public static string REBOOT_CMD = "SYST:REB";
+
+ // panel disable/enable commands
+ public static string SET_FRONTPANEL_DISABLE_CMD = "SYST:COMM:RLST RWL";
+ public static string SET_FRONTPANEL_ENABLE_CMD = "SYST:COMM:RLST REM";
+
+ // watchdog commands
+ public static string SET_WATCHDOGDELAY_CMD = "OUTP:PROT:WDOG:DEL";
+ public static string SET_WATCHDOGON_CMD = "OUTP:PROT:WDOG ON";
+ public static string SET_WATCHDOGOFF_CMD = "OUTP:PROT:WDOG OFF";
+
+ // coupling commands
+ public static string SET_COUPLE_CHANNELS_CMD = "OUTP:COUP:CHAN";
+ public static string SET_COUPLE_ON_CMD = "OUTP:COUP ON";
+ public static string SET_COUPLE_OUTPUT_PROTECT_ON_CMD = "OUTP:PROT:COUP ON";
+ public static string QUERY_COUPLE_CHANNELS = "OUTP:COUP:CHAN?";
+ public static string QUERY_COUPLE_STATE = "OUTP:COUP?";
+
+ // Grouping Commands
+ public static string SET_GROUP_DEFINE_CMD = "SYST:GRO:DEF";
+ public static string UNGROUP_ALL_CHANNELS_CMD = "SYST:GRO:DEL:ALL";
+ public static string QUERY_GROUP_CHANNELS = "SYST:GRO:CAT?";
+
+ // current commands
+ public static string SET_OCP_CMD = "CURR:LEV";
+ public static string SET_OCP_ON_CMD = "CURR:PROT:STAT ON";
+ public static string READ_CURRENT_CMD = "MEAS:CURR?";
+ public static string READ_OCP_CMD = "CURR:LEV?";
+
+ // voltage commands
+ public static string SET_OVP_CMD = "VOLT:PROT";
+ public static string SET_VOLTAGE_SLEW_RATE_CMD = "VOLT:SLEW";
+ public static string SET_VOLTAGE_SETPOINT_CMD = "VOLT:LEV";
+ public static string SET_CONSTANT_VOLTAGE_CMD = "STAT:OPER:ENAB 1";
+ public static string READ_VOLTAGE_CMD = "MEAS:VOLT?";
+ public static string READ_VOLTAGE_SETPOINT_CMD = "VOLT?";
+ public static string READ_OVP_CMD = "VOLT:PROT?";
+ public static string READ_VOLTAGE_SLEW_RATE_CMD = "VOLT:SLEW?";
+
+ // set output commands
+ public static string SET_OUTPUT_DISABLE_CMD = "OUTP OFF";
+ public static string SET_OUTPUT_ENABLE_CMD = "OUTP ON";
+
+ //query status
+ public static string READ_OUTPUT_STATUS_CMD = "OUTP?";
+ public static string READ_ERROR_STATUS_CMD = "SYST:ERR?";
+ public static string READ_PROTECTION_STATUS_CMD = "STAT:QUES:COND?";
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupply.cs b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupply.cs
new file mode 100644
index 0000000..960964b
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupply.cs
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments
+{
+ ///
+ /// Base class that defines interface for power supply systems
+ ///
+ public abstract class PowerSupply : Instrument
+ {
+ protected PowerSupplyModule _powerSupplyModule;
+
+ ///
+ /// Implement Indexer in order to access a specific power module
+ ///
+ ///
+ ///
+ public virtual PowerSupplyModule this[object i]
+ {
+ get { return null; }
+ }
+
+ ///
+ /// Get power supply module info dictionary
+ ///
+ ///
+ ///
+ public Dictionary GetPowerSupplyModuleInfoDict()
+ {
+ return _powerSupplyModule.PowerModuleInfoDict;
+ }
+
+ ///
+ /// Get power supply module names
+ ///
+ ///
+ ///
+ public List GetPowerSupplyModuleNames()
+ {
+ return _powerSupplyModule.PowerModules;
+ }
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyBasic.csproj b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyBasic.csproj
new file mode 100644
index 0000000..02bf060
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyBasic.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net472
+ Library
+ Raytheon.Instruments.PowerSupplyBasic
+ PowerSupply Basic
+ PowerSupply Basic
+
+ 1.1.0.0
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigIni.cs b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigIni.cs
new file mode 100644
index 0000000..c1c8c3d
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigIni.cs
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ public enum PowerSupplyConfigIni
+ {
+ // list all the sections here
+ GENERAL,
+
+ // list all the keys here
+ ETHERNET_ADDRESS,
+ ETHERNET_PORT,
+ MODULE_DEFINITION,
+ COUPLED_MODULES,
+ GROUPED_MODULES,
+ INDEX,
+ OCP,
+ OVP,
+ VOLTAGE_SETPOINT,
+ VOLTAGE_SLEW_RATE,
+ MIN_VOLTAGE,
+ MAX_VOLTAGE,
+ MIN_CURRENT,
+ MAX_CURRENT
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigXml.cs b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigXml.cs
new file mode 100644
index 0000000..676aed9
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyConfigXml.cs
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ public enum PowerSupplyConfigXml
+ {
+ // list all the keys here
+ INI_FILE_PATH
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModule.cs b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModule.cs
new file mode 100644
index 0000000..178fbd8
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModule.cs
@@ -0,0 +1,207 @@
+/*-------------------------------------------------------------------------
+// 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 Raytheon.Instruments.PowerSupplies;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments
+{
+ ///
+ /// Base class that defines interface for power supply modules
+ ///
+ public abstract class PowerSupplyModule
+ {
+ protected Semaphore SemObj = new System.Threading.Semaphore(initialCount: 1, maximumCount: 1);
+ protected object SyncObj = new object();
+ protected string ActivePowerModule;
+
+ public List PowerModules { get; protected set; }
+
+ public Dictionary PowerModuleInfoDict { get; protected set; }
+
+ ///
+ /// Constructor
+ ///
+ public PowerSupplyModule()
+ {
+ PowerModuleInfoDict = new Dictionary();
+ }
+
+ ///
+ /// DisplayEnabled - some instruments will be no-op, but likely needed on all
+ /// will shut down any display on the hardware to hide any
+ /// classified information if visible
+ ///
+ public virtual bool DisplayEnabled
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// FrontPanelEnabled - some instruments will be no-op, but likely needed on all
+ /// has the ability to disable any panel buttons, so that
+ /// the hardware may not be changed by human touch
+ ///
+ public virtual bool FrontPanelEnabled
+ {
+ private get;
+ set;
+ }
+
+ ///
+ /// SelfTestResult - end result of the hardware self test performed
+ ///
+ public SelfTestResult SelfTestResult
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// PerformSelfTest - do a hardware self test
+ ///
+ /// result of the self test
+ public abstract SelfTestResult PerformSelfTest();
+
+ ///
+ /// Set active module
+ ///
+ ///
+ ///
+ public void SetActivePowerModule(string activePowerModule)
+ {
+ ActivePowerModule = activePowerModule;
+ }
+
+ ///
+ /// Check if active module is valid
+ ///
+ ///
+ ///
+ protected void CheckActivePowerModuleValidity()
+ {
+ if (ActivePowerModule == null || ActivePowerModule.Length == 0)
+ throw new Exception($"{nameof(ActivePowerModule)} is not set");
+
+ if (!PowerModuleInfoDict.ContainsKey(ActivePowerModule))
+ throw new Exception($"Invalid power module: {ActivePowerModule}");
+ }
+
+ ///
+ /// Return semaphore
+ ///
+ ///
+ ///
+ public Semaphore GetSemphamore() { return SemObj; }
+
+ ///
+ /// Turn on power module's output
+ ///
+ ///
+ ///
+ public abstract void On();
+
+ ///
+ /// Turn off power module's output
+ ///
+ ///
+ ///
+ public abstract void Off();
+
+ ///
+ /// Determine if module's output is on
+ ///
+ ///
+ ///
+ protected virtual bool IsOutputOn()
+ {
+ bool isOn = false;
+
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ isOn = PowerModuleInfoDict[ActivePowerModule].isOn_;
+ }
+
+ return isOn;
+ }
+
+ ///
+ /// Read power supply module's data all at once
+ ///
+ ///
+ ///
+ public virtual void ReadPowerSupplyData(out double volts, out double current, out double voltSetpoint, out bool isOn, out int faultStatus)
+ {
+ volts = 0.0;
+ current = 0.0;
+ voltSetpoint = 0.0;
+ isOn = false;
+ faultStatus = 0;
+ try
+ {
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ volts = ReadVoltage();
+ current = ReadCurrent();
+ voltSetpoint = PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_;
+ isOn = IsOutputOn();
+ faultStatus = ReadProtectionStatus();
+ }
+ }
+ finally { SemObj?.Release(); };
+ }
+
+ ///
+ /// Read voltage
+ ///
+ ///
+ ///
+ protected abstract double ReadVoltage();
+
+ ///
+ /// Read current
+ ///
+ ///
+ ///
+ protected abstract double ReadCurrent();
+
+ ///
+ /// Read protection status
+ ///
+ ///
+ ///
+ protected abstract int ReadProtectionStatus();
+
+ ///
+ /// Get error code
+ ///
+ ///
+ ///
+ protected abstract string GetErrorCode(out int errorCode);
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModuleInfo.cs b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModuleInfo.cs
new file mode 100644
index 0000000..443722f
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplyBasic/PowerSupplyModuleInfo.cs
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ public class PowerSupplyModuleInfo
+ {
+ public PowerSupplyModuleInfo()
+ {
+ overCurrentProtection_ = -1.0;
+ overVoltageProtection_ = -1.0;
+ voltageSetpoint_ = -1.0;
+ voltageSlewRate_ = -1.0;
+ voltageLowerLimit_ = -1.0;
+ voltageUpperLimit_ = -1.0;
+ currentLowerLimit_ = -1.0;
+ currentUpperLimit_ = -1.0;
+ isOn_ = false;
+ faultStatus = -1;
+ }
+
+ public PowerSupplyModuleInfo(string moduleIndex, double overCurrentProtection, double overVoltageProtection, double voltageSetpoint, double voltageSlewRate, double minVoltage, double maxVoltage, double minCurrent, double maxCurrent)
+ {
+ overCurrentProtection_ = overCurrentProtection;
+ overVoltageProtection_ = overVoltageProtection;
+ voltageSetpoint_ = voltageSetpoint;
+ voltageSlewRate_ = voltageSlewRate;
+ voltageLowerLimit_ = minVoltage;
+ voltageUpperLimit_ = maxVoltage;
+ currentLowerLimit_ = minCurrent;
+ currentUpperLimit_ = maxCurrent;
+ isOn_ = false;
+ faultStatus = -1;
+
+ moduleNameFormat = "(@" + moduleIndex + ")";
+ }
+
+ public string moduleNameFormat;
+ public double overCurrentProtection_;
+ public double overVoltageProtection_;
+ public double voltageLowerLimit_;
+ public double voltageUpperLimit_;
+ public double currentLowerLimit_;
+ public double currentUpperLimit_;
+ public double voltageSetpoint_;
+ public double voltageSlewRate_;
+ public bool isOn_;
+ public int faultStatus;
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplyModuleSim.cs b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplyModuleSim.cs
new file mode 100644
index 0000000..b789ec5
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplyModuleSim.cs
@@ -0,0 +1,250 @@
+/*-------------------------------------------------------------------------
+// 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.Linq;
+using NLog;
+using Raytheon.Common;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ ///
+ /// Class to simulate any power supply module
+ ///
+ class PowerSupplyModuleSim : PowerSupplyModule
+ {
+ private List _groupedModules;
+ private List _coupledModules;
+ private string _powerSupplySystemName;
+ private bool _frontPanelEnabled = true;
+
+ private ILogger _logger;
+
+ ///
+ /// Constructor
+ ///
+ public PowerSupplyModuleSim(string iniFilePath, string powerSupplySystemName)
+ {
+ _logger = LogManager.GetCurrentClassLogger();
+ _powerSupplySystemName = powerSupplySystemName;
+
+ IConfigurationFile config = new ConfigurationFile(iniFilePath);
+
+ string moduleDef = config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.MODULE_DEFINITION.ToString());
+ string coupledModules = config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.COUPLED_MODULES.ToString());
+ string groupedModules = config.ReadValue(PowerSupplyConfigIni.GENERAL.ToString(), PowerSupplyConfigIni.GROUPED_MODULES.ToString());
+
+ PowerModules = moduleDef.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ _coupledModules = coupledModules.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ _groupedModules = groupedModules.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).ToList();
+
+ if (_groupedModules.Count() > 1)
+ {
+ PowerModules.Clear();
+ // since modules are grouped, we pick the first module as the representative module
+ PowerModules.Add(_groupedModules[0]);
+ }
+
+ // build the power module map
+ string moduleIndex = "";
+ double ovp = 0.0;
+ double ocp = 0.0;
+ double voltageSetPoint = 0.0;
+ double voltageSlewRate = 0.0;
+
+ double minVoltage = 0.0;
+ double maxVoltage = 0.0;
+ double minCurrent = 0.0;
+ double maxCurrent = 0.0;
+
+ for (int i = 0; i < PowerModules.Count(); i++)
+ {
+ string moduleName = PowerModules[i];
+
+ moduleIndex = config.ReadValue(moduleName, PowerSupplyConfigIni.INDEX.ToString());
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.OCP.ToString()), out ocp);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.OVP.ToString()), out ovp);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.VOLTAGE_SETPOINT.ToString()), out voltageSetPoint);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.VOLTAGE_SLEW_RATE.ToString()), out voltageSlewRate);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.MIN_VOLTAGE.ToString()), out minVoltage);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.MAX_VOLTAGE.ToString()), out maxVoltage);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.MIN_CURRENT.ToString()), out minCurrent);
+ Double.TryParse(config.ReadValue(moduleName, PowerSupplyConfigIni.MAX_CURRENT.ToString()), out maxCurrent);
+
+ PowerModuleInfoDict[moduleName] = new Raytheon.Instruments.PowerSupplies.PowerSupplyModuleInfo(moduleIndex, ocp, ovp, voltageSetPoint, voltageSlewRate, minVoltage, maxVoltage, minCurrent, maxCurrent);
+ }
+ }
+
+ ///
+ /// Enable or Disable Front Panel
+ ///
+ ///
+ ///
+ public override bool DisplayEnabled
+ {
+ set { SemObj?.Release(); }
+ }
+
+ ///
+ /// Enable or disable Front Panel
+ ///
+ ///
+ ///
+ public override bool FrontPanelEnabled
+ {
+ set {_frontPanelEnabled = value; SemObj?.Release(); }
+ }
+
+ ///
+ /// Turn on power module's output
+ ///
+ ///
+ ///
+ public override void On()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ if (_coupledModules.Contains(ActivePowerModule))
+ {
+ foreach (string module in _coupledModules)
+ {
+ PowerModuleInfoDict[module].isOn_ = true;
+ }
+ }
+ else
+ PowerModuleInfoDict[ActivePowerModule].isOn_ = true;
+ }
+ }
+ finally { SemObj?.Release(); }
+ }
+
+ ///
+ /// Turn off power module's output
+ ///
+ ///
+ ///
+ public override void Off()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ CheckActivePowerModuleValidity();
+
+ if (_coupledModules.Contains(ActivePowerModule))
+ {
+ foreach (string module in _coupledModules)
+ {
+ PowerModuleInfoDict[module].isOn_ = false;
+ }
+ }
+ else
+ PowerModuleInfoDict[ActivePowerModule].isOn_ = false;
+ }
+ }
+ finally { SemObj?.Release(); };
+ }
+
+ ///
+ /// Perform self test
+ ///
+ ///
+ ///
+ public override SelfTestResult PerformSelfTest()
+ {
+ try
+ {
+ lock (SyncObj)
+ {
+ SelfTestResult = SelfTestResult.Pass;
+ }
+ }
+ finally { SemObj?.Release(); };
+
+ return SelfTestResult;
+ }
+
+ ///
+ /// Read voltage
+ ///
+ ///
+ ///
+ protected override double ReadVoltage()
+ {
+ double val = 0.0;
+
+ lock (SyncObj)
+ {
+ if (PowerModuleInfoDict[ActivePowerModule].isOn_)
+ val = PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_;
+ }
+
+ return val;
+ }
+
+ ///
+ /// Read current
+ ///
+ ///
+ ///
+ protected override double ReadCurrent()
+ {
+ double val = 0.0;
+
+ lock (SyncObj)
+ {
+ if (PowerModuleInfoDict[ActivePowerModule].isOn_)
+ val = (PowerModuleInfoDict[ActivePowerModule].currentLowerLimit_ + PowerModuleInfoDict[ActivePowerModule].currentUpperLimit_) / 2.0;
+ }
+
+ return val;
+ }
+
+ ///
+ /// Read protection status
+ ///
+ ///
+ ///
+ protected override int ReadProtectionStatus()
+ {
+ lock (SyncObj)
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// Get error code
+ ///
+ ///
+ ///
+ protected override string GetErrorCode(out int errorCode)
+ {
+ lock (SyncObj)
+ {
+ errorCode = 0;
+ return "";
+ }
+ }
+
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.cs b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.cs
new file mode 100644
index 0000000..48d19a5
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.cs
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+// 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.IO;
+using System.Reflection;
+using NLog;
+using Raytheon.Common;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ ///
+ /// Class to simulate any power supply system
+ ///
+ public class PowerSupplySim : PowerSupply
+ {
+ private string _iniFilePath;
+
+ private static ILogger _logger;
+ private readonly IConfigurationManager _configurationManager;
+ private readonly IConfiguration _configuration;
+
+ ///
+ /// Constructor
+ ///
+ public PowerSupplySim(string deviceInstanceName, IConfigurationManager configurationManager, ILogger logger)
+ {
+ Name = deviceInstanceName;
+ _logger = logger;
+ _configurationManager = configurationManager;
+ _configuration = _configurationManager.GetConfiguration(Name);
+
+ string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ _iniFilePath = Path.Combine(assemblyFolder,_configuration.GetConfigurationValue(deviceInstanceName, "IniFilePath"));
+ }
+
+ ///
+ /// Perform shutdown
+ ///
+ ///
+ ///
+ public override void Shutdown()
+ {
+ }
+
+ ///
+ /// Perform reset
+ ///
+ ///
+ ///
+ public override void Reset()
+ {
+ }
+
+ ///
+ /// Clear errors
+ ///
+ ///
+ ///
+ public override bool ClearErrors()
+ {
+ return true;
+ }
+
+ ///
+ /// Group or couple modules as specified in config file
+ /// Gather information for each power module from config file
+ ///
+ ///
+ ///
+ public override void Initialize()
+ {
+ _powerSupplyModule = new PowerSupplyModuleSim(_iniFilePath, Name);
+ }
+
+ ///
+ /// Implement Indexer to obtain a power module
+ ///
+ ///
+ ///
+ public override PowerSupplyModule this[object powerDeviceId]
+ {
+ get
+ {
+ string powerDeviceName = String.Empty;
+
+ if (powerDeviceId != null && (powerDeviceId.GetType().IsEnum || powerDeviceId is string))
+ {
+ powerDeviceName = powerDeviceId.ToString();
+ }
+ else if (powerDeviceId != null)
+ {
+ throw new ArgumentException($"{nameof(powerDeviceId)} must be null or enumerated or string type");
+ }
+
+ _powerSupplyModule.GetSemphamore().WaitOne();
+
+ if (powerDeviceId != null)
+ _powerSupplyModule.SetActivePowerModule(powerDeviceName);
+
+ return _powerSupplyModule;
+ }
+ }
+ }
+}
diff --git a/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.csproj b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.csproj
new file mode 100644
index 0000000..87ccde8
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySim.csproj
@@ -0,0 +1,32 @@
+
+
+
+
+ net472
+ Library
+ Raytheon.Instruments.PowerSupplies.Simulation
+ Power Supply Simulation
+ Power Supply Simulation
+
+
+
+
+
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySimFactory.cs b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySimFactory.cs
new file mode 100644
index 0000000..fae00d2
--- /dev/null
+++ b/Source/DucLib/PowerSupplies/PowerSupplySim/PowerSupplySimFactory.cs
@@ -0,0 +1,114 @@
+/*-------------------------------------------------------------------------
+// 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 NLog;
+using Raytheon.Common;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Reflection;
+
+namespace Raytheon.Instruments.PowerSupplies
+{
+ [ExportInstrumentFactory(ModelNumber = "PowerSupplySimulationFactory")]
+ public class PowerSupplySimulationFactory : IInstrumentFactory
+ {
+ ///
+ /// The supported interfaces
+ ///
+ private readonly List _supportedInterfaces = new List();
+ private static ILogger _logger;
+ private readonly IConfigurationManager _configurationManager;
+ private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
+ private static string DefaultPath;
+
+ public PowerSupplySimulationFactory(string defaultConfigPath = DefaultConfigPath)
+ : this(null, defaultConfigPath)
+ {
+ }
+
+ ///
+ /// COECommDeviceInstrumentFactory injection constructor
+ ///
+ ///
+ ///
+ ///
+ [ImportingConstructor]
+ public PowerSupplySimulationFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
+ [Import(AllowDefault = true)] string defaultConfigPath = null)
+ {
+ DefaultPath = defaultConfigPath;
+ _logger = LogManager.GetCurrentClassLogger();
+
+ if (NLog.LogManager.Configuration == null)
+ {
+ var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
+ }
+
+ _configurationManager = configManager ?? GetConfigurationManager();
+ _supportedInterfaces.Add(typeof(PowerSupply));
+ }
+
+ ///
+ /// Gets the instrument
+ ///
+ ///
+ ///
+ public IInstrument GetInstrument(string name)
+ {
+ return null;
+ }
+
+ ///
+ /// Gets the instrument
+ ///
+ ///
+ ///
+ public object GetInstrument(string name, bool simulateHw)
+ {
+ try
+ {
+ return new PowerSupplySim(name, _configurationManager, _logger);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, $"Unable to construct {name} instrument instance");
+ return null;
+ }
+ }
+
+ ///
+ /// Gets supported interfaces
+ ///
+ ///
+ public ICollection GetSupportedInterfaces()
+ {
+ return _supportedInterfaces.ToArray();
+ }
+
+ ///
+ /// returns confiuration based on the predefined path or default path c:/ProgramData/Raytheon/InstrumentManagerService
+ ///
+ ///
+ private static IConfigurationManager GetConfigurationManager()
+ {
+ return string.IsNullOrEmpty(DefaultPath) ? new RaytheonConfigurationManager() : new RaytheonConfigurationManager(DefaultPath);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/DucLib/TcpClient/TcpClient.cs b/Source/DucLib/TcpClient/TcpClient.cs
new file mode 100644
index 0000000..16572e1
--- /dev/null
+++ b/Source/DucLib/TcpClient/TcpClient.cs
@@ -0,0 +1,226 @@
+/*-------------------------------------------------------------------------
+// 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.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Text.RegularExpressions;
+
+namespace Raytheon.Instruments.EthernetSockets
+{
+ ///
+ /// Class for controlling a TCP client communication device
+ ///
+ public class TcpClient
+ {
+ #region PrivateClassMembers
+
+ private Socket _sock;
+ private string _remoteAddress;
+ private int _remotePort;
+ private IPEndPoint _remoteEP = null;
+ private IPAddress _ipAddress = null;
+ private object _syncObj = new object();
+
+ #endregion
+
+ #region Public Functions
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public TcpClient(string remoteAddress, int remotePort)
+ {
+ _remoteAddress = remoteAddress;
+ _remotePort = remotePort;
+ }
+
+ ///
+ /// initialize instrument
+ ///
+ public void Initialize()
+ {
+ // if remoteAddress is a hostname
+ if (!IPAddress.TryParse(_remoteAddress, out _ipAddress))
+ {
+ string preferredSubnet = "";
+
+ IPHostEntry host = Dns.GetHostEntry(_remoteAddress);
+ foreach (IPAddress ip in host.AddressList)
+ {
+ AddressFamily af = ip.AddressFamily;
+ if (af == AddressFamily.InterNetwork)
+ {
+ if (preferredSubnet != String.Empty)
+ {
+ if (Regex.IsMatch(ip.ToString(), preferredSubnet, RegexOptions.IgnoreCase))
+ _ipAddress = ip;
+ }
+ else
+ _ipAddress = ip;
+
+ if (_ipAddress != null)
+ break;
+ }
+ }
+ }
+
+ if (_ipAddress != null)
+ {
+ if (_remoteEP == null)
+ _remoteEP = new IPEndPoint(_ipAddress, _remotePort);
+ }
+ else
+ throw new Exception($"Unable to create IPEndPoint to {_remoteAddress}, port {_remotePort}");
+
+ if (_sock == null)
+ _sock = new Socket(_ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ }
+
+ ///
+ /// Connect to remote host
+ ///
+ ///
+ public void Connect()
+ {
+ lock (_syncObj)
+ {
+ try
+ {
+ if (!_sock.Connected && IsRemoteHostAlive())
+ _sock.Connect(_remoteEP);
+ }
+ catch (ObjectDisposedException)
+ {
+ _sock = new Socket(_ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ if (IsRemoteHostAlive())
+ _sock.Connect(_remoteEP);
+ }
+ catch (Exception) { throw; }
+ }
+ }
+
+ ///
+ /// Disconnect from remote host
+ ///
+ ///
+ public void Disconnect()
+ {
+ lock (_syncObj)
+ {
+ if (_sock.Connected)
+ {
+ _sock.Shutdown(SocketShutdown.Both);
+ _sock.Disconnect(true);
+ _sock.Close();
+ }
+ }
+ }
+
+ ///
+ /// Ping if remote host is alive
+ ///
+ /// true/false
+ bool IsRemoteHostAlive()
+ {
+ bool isRemoteHostAlive = true;
+
+ //Do a ping test to see if the server is reachable
+ try
+ {
+ Ping pingTest = new Ping();
+ PingReply reply = pingTest.Send(_ipAddress);
+ if (reply.Status != IPStatus.Success)
+ isRemoteHostAlive = false;
+ }
+ catch (PingException)
+ {
+ isRemoteHostAlive = false;
+ }
+
+ //See if the tcp state is ok
+ if (_sock.Poll(5000, SelectMode.SelectRead) && (_sock.Available == 0))
+ {
+ isRemoteHostAlive = false;
+ }
+
+ return isRemoteHostAlive;
+ }
+
+ ///
+ /// Read data from the device.
+ ///
+ ///
+ ///
+ /// The number of bytes read
+ public uint Read(ref byte[] dataBuf)
+ {
+ int bytesRec = 0;
+ lock (_syncObj)
+ {
+ try
+ {
+ bytesRec = _sock.Receive(dataBuf);
+ }
+ catch (SocketException)
+ {
+ bytesRec = 0;
+ }
+ }
+
+ return (uint)bytesRec;
+ }
+
+ ///
+ /// Sets the read timeout
+ ///
+ ///
+ public void SetReadTimeout(uint timeoutMs)
+ {
+ _sock.ReceiveTimeout = (int)timeoutMs;
+ }
+
+ ///
+ /// Write data to device.
+ ///
+ ///
+ /// The number of bytes written
+ public uint Write(byte[] dataBuf, uint numBytesToWrite)
+ {
+ int bytesWritten = 0;
+ lock (_syncObj)
+ {
+ try
+ {
+ bytesWritten = _sock.Send(dataBuf, (int)numBytesToWrite, SocketFlags.None);
+ }
+ catch (SocketException)
+ {
+ bytesWritten = 0;
+ }
+ }
+
+ return (uint)bytesWritten;
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/DucLib/TcpClient/TcpClient.csproj b/Source/DucLib/TcpClient/TcpClient.csproj
new file mode 100644
index 0000000..0e8afd5
--- /dev/null
+++ b/Source/DucLib/TcpClient/TcpClient.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+ net472
+ Library
+ TcpClient
+ TCP Client implementation
+ TCP Client implementation
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+
+
+ 1.0.0
+ Debug;Release;Deploy
+
+
+
diff --git a/Source/Program.sln b/Source/Program.sln
index 02375ec..4441917 100644
--- a/Source/Program.sln
+++ b/Source/Program.sln
@@ -362,6 +362,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerSupplyMeasurementManag
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Program", "Program\Program.csproj", "{FAFB2DB1-AF3A-4F78-8BBB-124C51C4D62A}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DucLib", "DucLib", "{F20B097A-6283-426E-A9B5-D71E498E4A78}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpClient", "DucLib\TcpClient\TcpClient.csproj", "{4D2E2A24-9FD4-49C9-8448-F7006ED790B8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeysightN67XX", "DucLib\PowerSupplies\Keysight67XX\KeysightN67XX.csproj", "{CCD1C6AA-2C4E-40E1-8233-20875B5833D3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerSupplySim", "DucLib\PowerSupplies\PowerSupplySim\PowerSupplySim.csproj", "{059FF15F-D78A-4727-BA84-16836EB9F2DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerSupplyBasic", "DucLib\PowerSupplies\PowerSupplyBasic\PowerSupplyBasic.csproj", "{675101BD-867F-4268-838B-F1E673B9007A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerSupplies", "PowerSupplies", "{C2277D2F-C982-420A-AD8D-82452A83793B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1071,6 +1083,30 @@ Global
{FAFB2DB1-AF3A-4F78-8BBB-124C51C4D62A}.Deploy|Any CPU.Build.0 = Deploy|Any CPU
{FAFB2DB1-AF3A-4F78-8BBB-124C51C4D62A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAFB2DB1-AF3A-4F78-8BBB-124C51C4D62A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Deploy|Any CPU.ActiveCfg = Deploy|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Deploy|Any CPU.Build.0 = Deploy|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Deploy|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Deploy|Any CPU.Build.0 = Debug|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Deploy|Any CPU.ActiveCfg = Deploy|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Deploy|Any CPU.Build.0 = Deploy|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Deploy|Any CPU.ActiveCfg = Deploy|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Deploy|Any CPU.Build.0 = Deploy|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {675101BD-867F-4268-838B-F1E673B9007A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1223,6 +1259,11 @@ Global
{9BE7316F-AA0D-46C5-9AC4-1038697884A0} = {22A60CBA-5ADA-47C6-95B4-EAEC014EC878}
{C402EA41-F756-4270-946C-D3DC4E1BAEB5} = {CE2D75B9-FC4A-41C6-84B8-AD838A70C1FF}
{FAFB2DB1-AF3A-4F78-8BBB-124C51C4D62A} = {4D2003A8-CBCB-498C-9186-355650A84D41}
+ {4D2E2A24-9FD4-49C9-8448-F7006ED790B8} = {F20B097A-6283-426E-A9B5-D71E498E4A78}
+ {CCD1C6AA-2C4E-40E1-8233-20875B5833D3} = {C2277D2F-C982-420A-AD8D-82452A83793B}
+ {059FF15F-D78A-4727-BA84-16836EB9F2DF} = {C2277D2F-C982-420A-AD8D-82452A83793B}
+ {675101BD-867F-4268-838B-F1E673B9007A} = {C2277D2F-C982-420A-AD8D-82452A83793B}
+ {C2277D2F-C982-420A-AD8D-82452A83793B} = {F20B097A-6283-426E-A9B5-D71E498E4A78}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CA9C7EC5-FDF7-444C-99C8-7BDBE8AF4EDB}