My improved version of PowerSupply and TcpClient implementation

This commit is contained in:
Duc
2025-03-13 17:41:23 -07:00
parent 6419acb073
commit fd85735c93
18 changed files with 2249 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputType>Library</OutputType>
<AssemblyName>Raytheon.Instruments.PowerSupplies.Keysight67XX</AssemblyName>
<Product>Keysight 67XX Series Power Supply</Product>
<Description>Keysight 67XX Series Power Supply</Description>
<!-- Dynamic Versioning (Suitable for Release) -->
<!-- <Version>$(Version)$(Suffix)</Version> -->
<!-- Static Versioning (Suitable for Development) -->
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\TcpClient\TcpClient.csproj" />
<ProjectReference Include="..\PowerSupplySim\PowerSupplySim.csproj" />
</ItemGroup>
</Project>

View File

@@ -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
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
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)
{
}
/// <summary>
/// COECommDeviceInstrumentFactory injection constructor
/// </summary>
/// <param name="configManager"></param>
/// <param name="simEngine"></param>
/// <param name="logger"></param>
[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));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
return null;
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns confiuration based on the predefined path or default path c:/ProgramData/Raytheon/InstrumentManagerService
/// </summary>
/// <returns></returns>
private static IConfigurationManager GetConfigurationManager()
{
return string.IsNullOrEmpty(DefaultPath) ? new RaytheonConfigurationManager() : new RaytheonConfigurationManager(DefaultPath);
}
}
}

View File

@@ -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
{
/// <summary>
/// Class to simulate any power supply module
/// </summary>
class KeysightN67xxPowerModule : PowerSupplyModule
{
private List<string> _groupedModules;
private List<string> _coupledModules;
private string _powerSupplySystemName;
private EthernetSockets.TcpClient _tcpClient;
IConfigurationFile _config;
private ILogger _logger;
/// <summary>
/// Constructor
/// </summary>
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();
}
}
/// <summary>
/// Enable or Disable Front Panel
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override bool DisplayEnabled
{
set
{
SemObj?.Release();
}
}
/// <summary>
/// Enable or disable Front Panel
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
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(); }
}
}
/// <summary>
/// Turn on power module's output
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override void On()
{
try
{
lock (SyncObj)
{
CheckActivePowerModuleValidity();
string command = KeysightPowerSupplyScpiCommands.SET_OUTPUT_ENABLE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
SendCommand(command);
}
}
finally { SemObj?.Release(); }
}
/// <summary>
/// Turn off power module's output
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override void Off()
{
try
{
lock (SyncObj)
{
CheckActivePowerModuleValidity();
string command = KeysightPowerSupplyScpiCommands.SET_OUTPUT_DISABLE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
SendCommand(command);
}
}
finally { SemObj?.Release(); }
}
/// <summary>
/// Perform self test
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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;
}
/// <summary>
/// Set constant voltage mode
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private void SetConstantVoltageMode()
{
lock (SyncObj)
{
string command = KeysightPowerSupplyScpiCommands.SET_CONSTANT_VOLTAGE_CMD + "," + PowerModuleInfoDict[ActivePowerModule].moduleNameFormat + "\n";
SendCommand(command);
}
}
/// <summary>
/// Set and Confirm OVP
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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}");
}
}
/// <summary>
/// Set and Confirm OCP
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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}");
}
}
/// <summary>
/// Set and Confirm Voltage Setpoint
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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}");
}
}
/// <summary>
/// Set and Confirm Slew Rate
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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}");
}
}
}
/// <summary>
/// Get error code
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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;
}
/// <summary>
/// Read voltage
/// </summary>
/// <param></param>
/// <returns></returns>
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;
}
/// <summary>
/// Read current
/// </summary>
/// <param></param>
/// <returns></returns>
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;
}
/// <summary>
/// Read protection status
/// </summary>
/// <param></param>
/// <returns></returns>
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;
}
/// <summary>
/// Determine if module's output is on
/// </summary>
/// <param></param>
/// <returns></returns>
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;
}
/// <summary>
/// When modules are grouped, they act as one module
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private void GroupModules(List<string> 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;
}
}
}
/// <summary>
/// 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
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private void CoupleModules(List<string> 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;
}
}
/// <summary>
/// Reset Power Supply System
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private void ResetPowerSupplySystem()
{
// send the command
string command = KeysightPowerSupplyScpiCommands.RESET_CMD + "\n";
SendCommand(command);
}
/// <summary>
/// Send command
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private void SendCommand(string command)
{
lock (SyncObj)
{
byte[] msg = Encoding.ASCII.GetBytes(command);
_tcpClient.Write(msg, (uint)msg.Length);
}
}
/// <summary>
/// Send command and get response
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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);
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Class for controlling a Keysightt N6700 Series Power Supply System
/// </summary>
public class KeysightN67xxPowerSupply : PowerSupply
{
private string _iniFilePath;
private static ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
/// <summary>
/// Constructor
/// </summary>
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()));
}
/// <summary>
/// Perform shutdown
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Shutdown()
{
}
/// <summary>
/// Perform reset
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Reset()
{
}
/// <summary>
/// Clear errors
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override bool ClearErrors()
{
return true;
}
/// <summary>
/// Implement Indexer in order to access a specific power module
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Group or couple modules as specified in config file
/// Gather information for each power module from config file
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Initialize()
{
_powerSupplyModule = new KeysightN67xxPowerModule(_iniFilePath, Name);
}
}
}

View File

@@ -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?";
}
}

View File

@@ -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
{
/// <summary>
/// Base class that defines interface for power supply systems
/// </summary>
public abstract class PowerSupply : Instrument
{
protected PowerSupplyModule _powerSupplyModule;
/// <summary>
/// Implement Indexer in order to access a specific power module
/// </summary>
/// <param></param>
/// <returns></returns>
public virtual PowerSupplyModule this[object i]
{
get { return null; }
}
/// <summary>
/// Get power supply module info dictionary
/// </summary>
/// <param></param>
/// <returns></returns>
public Dictionary<string, PowerSupplies.PowerSupplyModuleInfo> GetPowerSupplyModuleInfoDict()
{
return _powerSupplyModule.PowerModuleInfoDict;
}
/// <summary>
/// Get power supply module names
/// </summary>
/// <param></param>
/// <returns></returns>
public List<string> GetPowerSupplyModuleNames()
{
return _powerSupplyModule.PowerModules;
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputType>Library</OutputType>
<AssemblyName>Raytheon.Instruments.PowerSupplyBasic</AssemblyName>
<Description>PowerSupply Basic</Description>
<Product>PowerSupply Basic</Product>
<Version>1.1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Raytheon.Communication.Rpc.Contracts" Version="1.*" />
<PackageReference Include="Raytheon.Communication.Ums.Core.Contracts" Version="1.*" />
<PackageReference Include="Raytheon.Communication.Ums.Rpc.Attributes" Version="1.*" />
<PackageReference Include="Raytheon.Instruments.Contracts" Version="1.6.0" />
</ItemGroup>
</Project>

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
{
/// <summary>
/// Base class that defines interface for power supply modules
/// </summary>
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<string> PowerModules { get; protected set; }
public Dictionary<string, PowerSupplyModuleInfo> PowerModuleInfoDict { get; protected set; }
/// <summary>
/// Constructor
/// </summary>
public PowerSupplyModule()
{
PowerModuleInfoDict = new Dictionary<string, PowerSupplyModuleInfo>();
}
/// <summary>
/// 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
/// </summary>
public virtual bool DisplayEnabled
{
get;
set;
}
/// <summary>
/// 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
/// </summary>
public virtual bool FrontPanelEnabled
{
private get;
set;
}
/// <summary>
/// SelfTestResult - end result of the hardware self test performed
/// </summary>
public SelfTestResult SelfTestResult
{
get;
set;
}
/// <summary>
/// PerformSelfTest - do a hardware self test
/// </summary>
/// <returns>result of the self test</returns>
public abstract SelfTestResult PerformSelfTest();
/// <summary>
/// Set active module
/// </summary>
/// <param></param>
/// <returns></returns>
public void SetActivePowerModule(string activePowerModule)
{
ActivePowerModule = activePowerModule;
}
/// <summary>
/// Check if active module is valid
/// </summary>
/// <param></param>
/// <returns></returns>
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}");
}
/// <summary>
/// Return semaphore
/// </summary>
/// <param></param>
/// <returns></returns>
public Semaphore GetSemphamore() { return SemObj; }
/// <summary>
/// Turn on power module's output
/// </summary>
/// <param></param>
/// <returns></returns>
public abstract void On();
/// <summary>
/// Turn off power module's output
/// </summary>
/// <param></param>
/// <returns></returns>
public abstract void Off();
/// <summary>
/// Determine if module's output is on
/// </summary>
/// <param></param>
/// <returns></returns>
protected virtual bool IsOutputOn()
{
bool isOn = false;
lock (SyncObj)
{
CheckActivePowerModuleValidity();
isOn = PowerModuleInfoDict[ActivePowerModule].isOn_;
}
return isOn;
}
/// <summary>
/// Read power supply module's data all at once
/// </summary>
/// <param></param>
/// <returns></returns>
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(); };
}
/// <summary>
/// Read voltage
/// </summary>
/// <param></param>
/// <returns></returns>
protected abstract double ReadVoltage();
/// <summary>
/// Read current
/// </summary>
/// <param></param>
/// <returns></returns>
protected abstract double ReadCurrent();
/// <summary>
/// Read protection status
/// </summary>
/// <param></param>
/// <returns></returns>
protected abstract int ReadProtectionStatus();
/// <summary>
/// Get error code
/// </summary>
/// <param></param>
/// <returns></returns>
protected abstract string GetErrorCode(out int errorCode);
}
}

View File

@@ -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;
}
}

View File

@@ -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
{
/// <summary>
/// Class to simulate any power supply module
/// </summary>
class PowerSupplyModuleSim : PowerSupplyModule
{
private List<string> _groupedModules;
private List<string> _coupledModules;
private string _powerSupplySystemName;
private bool _frontPanelEnabled = true;
private ILogger _logger;
/// <summary>
/// Constructor
/// </summary>
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);
}
}
/// <summary>
/// Enable or Disable Front Panel
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override bool DisplayEnabled
{
set { SemObj?.Release(); }
}
/// <summary>
/// Enable or disable Front Panel
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override bool FrontPanelEnabled
{
set {_frontPanelEnabled = value; SemObj?.Release(); }
}
/// <summary>
/// Turn on power module's output
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
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(); }
}
/// <summary>
/// Turn off power module's output
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
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(); };
}
/// <summary>
/// Perform self test
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override SelfTestResult PerformSelfTest()
{
try
{
lock (SyncObj)
{
SelfTestResult = SelfTestResult.Pass;
}
}
finally { SemObj?.Release(); };
return SelfTestResult;
}
/// <summary>
/// Read voltage
/// </summary>
/// <param></param>
/// <returns></returns>
protected override double ReadVoltage()
{
double val = 0.0;
lock (SyncObj)
{
if (PowerModuleInfoDict[ActivePowerModule].isOn_)
val = PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_;
}
return val;
}
/// <summary>
/// Read current
/// </summary>
/// <param></param>
/// <returns></returns>
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;
}
/// <summary>
/// Read protection status
/// </summary>
/// <param></param>
/// <returns></returns>
protected override int ReadProtectionStatus()
{
lock (SyncObj)
{
return 0;
}
}
/// <summary>
/// Get error code
/// </summary>
/// <param name=""></param>
/// <returns></returns>
protected override string GetErrorCode(out int errorCode)
{
lock (SyncObj)
{
errorCode = 0;
return "";
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Class to simulate any power supply system
/// </summary>
public class PowerSupplySim : PowerSupply
{
private string _iniFilePath;
private static ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
/// <summary>
/// Constructor
/// </summary>
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"));
}
/// <summary>
/// Perform shutdown
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Shutdown()
{
}
/// <summary>
/// Perform reset
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Reset()
{
}
/// <summary>
/// Clear errors
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override bool ClearErrors()
{
return true;
}
/// <summary>
/// Group or couple modules as specified in config file
/// Gather information for each power module from config file
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public override void Initialize()
{
_powerSupplyModule = new PowerSupplyModuleSim(_iniFilePath, Name);
}
/// <summary>
/// Implement Indexer to obtain a power module
/// </summary>
/// <param name=""></param>
/// <returns></returns>
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;
}
}
}
}

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputType>Library</OutputType>
<AssemblyName>Raytheon.Instruments.PowerSupplies.Simulation</AssemblyName>
<Product>Power Supply Simulation</Product>
<Description>Power Supply Simulation</Description>
<!-- Dynamic Versioning (Suitable for Release) -->
<!-- <Version>$(Version)$(Suffix)</Version> -->
<!-- Static Versioning (Suitable for Development) -->
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.0" />
<PackageReference Include="Raytheon.Common" Version="1.0.0" />
<!--
<PackageReference Include="Raytheon.Instruments.PowerSupply.Contracts" Version="1.1.0" />
-->
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PowerSupplyBasic\PowerSupplyBasic.csproj" />
</ItemGroup>
</Project>

View File

@@ -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
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
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)
{
}
/// <summary>
/// COECommDeviceInstrumentFactory injection constructor
/// </summary>
/// <param name="configManager"></param>
/// <param name="simEngine"></param>
/// <param name="logger"></param>
[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));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
return null;
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns confiuration based on the predefined path or default path c:/ProgramData/Raytheon/InstrumentManagerService
/// </summary>
/// <returns></returns>
private static IConfigurationManager GetConfigurationManager()
{
return string.IsNullOrEmpty(DefaultPath) ? new RaytheonConfigurationManager() : new RaytheonConfigurationManager(DefaultPath);
}
}
}

View File

@@ -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
{
/// <summary>
/// Class for controlling a TCP client communication device
/// </summary>
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
/// <summary>
/// Constructor
/// </summary>
/// <param name="remoteAddress"></param>
/// <param name="remotePort"></param>
public TcpClient(string remoteAddress, int remotePort)
{
_remoteAddress = remoteAddress;
_remotePort = remotePort;
}
/// <summary>
/// initialize instrument
/// </summary>
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);
}
/// <summary>
/// Connect to remote host
/// </summary>
/// <returns></returns>
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; }
}
}
/// <summary>
/// Disconnect from remote host
/// </summary>
/// <returns></returns>
public void Disconnect()
{
lock (_syncObj)
{
if (_sock.Connected)
{
_sock.Shutdown(SocketShutdown.Both);
_sock.Disconnect(true);
_sock.Close();
}
}
}
/// <summary>
/// Ping if remote host is alive
/// </summary>
/// <returns>true/false</returns>
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;
}
/// <summary>
/// Read data from the device.
/// </summary>
/// <param name="dataBuf"></param>
/// <param name="responseTimeOutMs"></param>
/// <returns>The number of bytes read</returns>
public uint Read(ref byte[] dataBuf)
{
int bytesRec = 0;
lock (_syncObj)
{
try
{
bytesRec = _sock.Receive(dataBuf);
}
catch (SocketException)
{
bytesRec = 0;
}
}
return (uint)bytesRec;
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
_sock.ReceiveTimeout = (int)timeoutMs;
}
/// <summary>
/// Write data to device.
/// </summary>
/// <param name="dataBuf"></param>
/// <returns>The number of bytes written</returns>
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
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputType>Library</OutputType>
<AssemblyName>TcpClient</AssemblyName>
<Product>TCP Client implementation</Product>
<Description>TCP Client implementation</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<!-- Dynamic Versioning (Suitable for Release) -->
<!-- <Version>$(Version)$(Suffix)</Version> -->
<!-- Static Versioning (Suitable for Development) -->
<Version>1.0.0</Version>
<Configurations>Debug;Release;Deploy</Configurations>
</PropertyGroup>
</Project>

View File

@@ -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}