Files
2025-10-24 15:18:11 -07:00

942 lines
29 KiB
C#

// UNCLASSIFIED
/*-------------------------------------------------------------------------
RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
COMPANY.
THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
GOVERNMENT.
UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.IO.Ports;
using NationalInstruments.NI4882;
using NLog;
using Raytheon.Common;
using Raytheon.Units;
namespace Raytheon.Instruments
{
/// <summary>
/// This class interfaces to a Keysight N3300 Eload system
/// </summary>
public class EloadSystemScpiKeysight : IELoadSystem
{
#region PrivateClassMembers
private enum ControlInterface
{
COM_PORT,
GPIB
}
// system commands
private const string _CLEAREVENTREG = "*CLS";
private const string _CLEARSERVICEREQUEST = "*SRE 0";
private const string _CLEAREVENTSTATUSENABLEREGISTER = "*ESE 0";
private const string _ERRORCODE = "SYST:ERR?";
private const string _RESET = "*RST";
private const string _SELFTEST = "*TST?";
private string _systemName;
private SerialPort _serialPort;
private Device _gpibDevice;
private ControlInterface _interface;
private readonly SortedDictionary<string, IEload> _eloadChannelMap;
private readonly bool _isThereHardware;
private static object _syncObj = new Object();
// default the timeout for responses to 5 seconds
private const int _READ_TIMEOUT = 5000;
private readonly ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
public string DetailedStatus { get; protected set; }
public bool DisplayEnabled { get; set; }
public bool FrontPanelEnabled { get; set; }
public InstrumentMetadata Info { get; set; }
public string Name { get; protected set; }
public SelfTestResult SelfTestResult => PerformSelfTest();
public State Status { get; set; }
#endregion
#region PrivateFuctions
/// <summary>
/// Send a SCPI Command to the instrument and get a response.
/// </summary>
/// <param name="commandString">The command to send.</param>
/// <returns>The instrument response.</returns>
private string IOQuery(string commandString)
{
// not calling IOWrite() so IOWrite() can check for errors after each write
string rsp = "";
if (_interface == ControlInterface.COM_PORT)
{
_serialPort.WriteLine(commandString);
rsp = _serialPort.ReadLine();
}
else if (_interface == ControlInterface.GPIB)
{
_gpibDevice.Write(commandString);
rsp = _gpibDevice.ReadString();
}
else
{
throw new Exception("unknown interface type: " + _interface.ToString());
}
rsp = rsp.Replace("\r", "");
// check for errors
int err = 0;
string errorMsg = GetErrorCode(out err);
if (err != 0)
{
throw new Exception(errorMsg);
}
return rsp;
}
/// <summary>
/// Sends a SCPI Command to the instrument.
/// </summary>
/// <param name="commandString">The command to send.</param>
private void IOWrite(string commandString)
{
if (_interface == ControlInterface.COM_PORT)
{
_serialPort.WriteLine(commandString);
}
else if (_interface == ControlInterface.GPIB)
{
_gpibDevice.Write(commandString);
}
else
{
throw new Exception("unknown interface type: " + _interface.ToString());
}
int err = 0;
string errorMsg = GetErrorCode(out err);
if (err != 0)
{
throw new Exception(errorMsg);
}
}
/// <summary>
/// Resets the instrument and clears event registers, error queue,
/// service request enable register, and event status enable register.
/// </summary>
private void Reset()
{
IOWrite(_RESET);
IOWrite(_CLEAREVENTREG);
IOWrite(_CLEARSERVICEREQUEST);
IOWrite(_CLEAREVENTSTATUSENABLEREGISTER);
}
#endregion
#region PublicFuctions
/// <summary>
/// FlowMeterOmegaDPF20 factory constructor
/// </summary>
/// <param name="deviceName"></param>
/// <param name="configurationManager"></param>
public EloadSystemScpiKeysight(string deviceName, IConfigurationManager configurationManager)
{
Name = deviceName;
_logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}");
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
_gpibDevice = null;
_serialPort = _configuration.GetConfigurationValue("ELoadScpiKeysight", "SerialPort", new SerialPort());
var flowControl = _configuration.GetConfigurationValue("ELoadScpiKeysight", "FlowControl", Handshake.None);
try
{
_interface = ControlInterface.COM_PORT;
_systemName = Name;
_serialPort.Open();
_serialPort.ReadTimeout = _READ_TIMEOUT;
_serialPort.Handshake = flowControl;
Reset();
_eloadChannelMap = new SortedDictionary<string, IEload>(StringComparer.InvariantCultureIgnoreCase);
}
catch (Exception)
{
if (_serialPort.IsOpen == true)
{
_serialPort.Close();
}
throw;
}
}
/// <summary>
/// The constructor for com port interface which open up the comm port, resets the system and waits for additional commands
/// </summary>
/// <param name="comPortName">The COM port name as it exists in windows device manager.</param>
/// <param name="baudRate">The bit rate of the host computer COM port.</param>
/// <param name="parity">The parity setting of the host computer. 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space.</param>
/// <param name="dataBits">The number of bits of data sent per transfer.</param>
/// <param name="stopBits">The number of stop bits used by host computer.</param>
/// <param name ="flowControl">Handshake method. 0 = None, 1 = XonXoff, 2 = RTS, 3 = RTSXonXoff.</param>
/// <param name="isThereHardware">Software operation mode. True = Hardware available, False = Simulation Only.</param>
public EloadSystemScpiKeysight(string systemName, string comPortName, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake flowControl, bool isThereHardware)
{
try
{
_logger = LogManager.GetCurrentClassLogger();
_interface = ControlInterface.COM_PORT;
_systemName = systemName;
_isThereHardware = isThereHardware;
_serialPort = null;
_gpibDevice = null;
if (_isThereHardware == true)
{
_serialPort = new SerialPort(comPortName, baudRate, parity, dataBits, stopBits);
_serialPort.Open();
_serialPort.ReadTimeout = _READ_TIMEOUT;
_serialPort.Handshake = flowControl;
Reset();
}
_eloadChannelMap = new SortedDictionary<string, IEload>(StringComparer.InvariantCultureIgnoreCase);
}
catch (Exception ex)
{
if (_serialPort.IsOpen == true)
{
_serialPort.Close();
}
_logger.Error(ex.Message);
throw;
}
}
/// <summary>
/// The constructor for gpib interface which open up the comm port, resets the system and waits for additional commands
/// </summary>
/// <param name="systemName"></param>
/// <param name="boardNumber"></param>
/// <param name="primaryAddress"></param>
/// <param name="isThereHardware"></param>
public EloadSystemScpiKeysight(string systemName, int boardNumber, byte primaryAddress, bool isThereHardware)
{
try
{
_interface = ControlInterface.GPIB;
_systemName = systemName;
_isThereHardware = isThereHardware;
_serialPort = null;
_gpibDevice = null;
if (_isThereHardware == true)
{
_gpibDevice = new Device(boardNumber, primaryAddress);
//@@@ TBD if this make the newline go out after each write
_gpibDevice.EndOfStringCharacter = 0xa;
_gpibDevice.IOTimeout = TimeoutValue.T3s;
Reset();
}
_eloadChannelMap = new SortedDictionary<string, IEload>(StringComparer.InvariantCultureIgnoreCase);
}
catch (Exception)
{
if (_gpibDevice != null)
{
_gpibDevice.Dispose();
_gpibDevice = null;
}
throw;
}
}
/// <summary>
/// The finalizer
/// </summary>
~EloadSystemScpiKeysight()
{
Dispose(false);
}
/// <summary>
/// Add an Eload to the system.
/// </summary>
/// <param name="name">The name of the channel (module) for reference.</param>
/// <param name="channelNumber">The channel number for the Eload..</param>
/// <param name="mode">The operation mode of the channel. Modes: Resistance, Voltage, Current.</param>
/// <param name="setpoint">The operation point of the load. This can be a voltage, current, or resistance</param>
/// <param name="overCurrentProtection">Overcurrent setpoint that will turn off the channel if exceeded.</param>
/// <param name="overVoltageProtection">Overvoltage setpoint that will turn off channel if exceeded (double check).</param>
public void AddEloadChannel(string name, int channelNumber, EloadModuleMode mode, double setpoint, Current overCurrentProtection, Voltage overVoltageProtection)
{
try
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(name.ToUpper()) == true)
{
throw new Exception("system already contains a module named: " + name.ToUpper());
}
IEload eload;
if (_isThereHardware)
{
if (_interface == ControlInterface.COM_PORT)
{
eload = new ELoadScpiKeysight(_serialPort, channelNumber, mode, setpoint, overCurrentProtection.Amps, overVoltageProtection.Volts);
}
else if (_interface == ControlInterface.GPIB)
{
eload = new ELoadScpiKeysight(_gpibDevice, channelNumber, mode, setpoint, overCurrentProtection.Amps, overVoltageProtection.Volts);
}
else
{
throw new Exception("unknown interface: " + _interface.ToString());
}
}
else
{
eload = new EloadSim(channelNumber, mode, setpoint, overCurrentProtection.Amps, overVoltageProtection.Volts);
}
_eloadChannelMap.Add(name.ToUpper(), eload);
}
}
catch (Exception)
{
if (_interface == ControlInterface.COM_PORT)
{
_serialPort.Close();
}
else if (_interface == ControlInterface.GPIB)
{
_gpibDevice.Dispose();
_gpibDevice = null;
}
throw;
}
}
/// <summary>
/// Dispose of this object's resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Get the error code.
/// </summary>
/// <returns>The error code.</returns>
public string GetErrorCode(out int errorCode)
{
lock (_syncObj)
{
if (_isThereHardware == true)
{
// not calling IOQuery() here so IOQuery() can call GetErrorCode() after each query
string rsp = "";
if (_interface == ControlInterface.COM_PORT)
{
_serialPort.WriteLine(_ERRORCODE);
rsp = _serialPort.ReadLine();
}
else if (_interface == ControlInterface.GPIB)
{
_gpibDevice.Write(_ERRORCODE);
rsp = _gpibDevice.ReadString();
}
else
{
throw new Exception("unknown interface type: " + _interface.ToString());
}
rsp = rsp.Replace("\r", "");
string[] tokens = rsp.Split(',');
errorCode = Util.ConvertStringToInt32(tokens[0]);
// it should always be 2
if (tokens.Length >= 2)
{
return tokens[1];
}
else
{
return "";
}
}
else
{
errorCode = 0;
return "";
}
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public List<string> GetModuleNames()
{
lock (_syncObj)
{
List<string> moduleNames = new List<string>();
foreach (KeyValuePair<string, IEload> modules in _eloadChannelMap)
{
moduleNames.Add(modules.Key);
}
return moduleNames;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public string GetSystemName()
{
lock (_syncObj)
{
return _systemName;
}
}
/// <summary>
/// Query if the Eload input is on.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>Status of Eload. True = On, False = Off.</returns>
public bool IsInputOn(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
bool status = _eloadChannelMap[channelName.ToUpper()].IsInputOn();
return status;
}
}
/// <summary>
/// Turn off the Eload input.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
public void Disable(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
_eloadChannelMap[channelName.ToUpper()].Disable();
}
}
/// <summary>
/// Turn on the Eload input.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
public void Enable(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
_eloadChannelMap[channelName.ToUpper()].Enable();
}
}
/// <summary>
///
/// </summary>
/// <param name="channelName"></param>
/// <param name="command"></param>
/// <returns></returns>
public string IOQuery(string channelName, string command)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
return _eloadChannelMap[channelName.ToUpper()].IOQuery(command);
}
}
/// <summary>
///
/// </summary>
/// <param name="channelName"></param>
/// <param name="command"></param>
public void IOWrite(string channelName, string command)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
_eloadChannelMap[channelName.ToUpper()].IOWrite(command);
}
}
/// <summary>
/// Reads the current of the Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>Measured current (Amps).</returns>
public Current ReadCurrent(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
var value = _eloadChannelMap[channelName.ToUpper()].ReadCurrent();
return value;
}
}
/// <summary>
/// Reads all Eload data (voltage, current, resistance, setpoint, input state, mode).
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <param name="voltage">The measured voltage (Volts).</param>
/// <param name="current">The measured current (Amps).</param>
/// <param name="resistance">The measured resistance (Ohms).</param>
/// <param name="setpoint">The setpoint of the Eload. Depends on mode of operation.</param>
/// <param name="isInputOn">The state of the Eload.</param>
/// <param name="mode">The mode of the Eload.</param>
public void ReadData(string channelName, out Voltage voltage, out Current current, out Resistance resistance, out double setpoint, out bool isInputOn, out EloadModuleMode mode, out ushort status)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
voltage = _eloadChannelMap[channelName.ToUpper()].ReadVoltage();
current = _eloadChannelMap[channelName.ToUpper()].ReadCurrent();
resistance = _eloadChannelMap[channelName.ToUpper()].ReadResistance();
setpoint = _eloadChannelMap[channelName.ToUpper()].ReadSetpoint();
isInputOn = _eloadChannelMap[channelName.ToUpper()].IsInputOn();
mode = _eloadChannelMap[channelName.ToUpper()].ReadMode();
status = _eloadChannelMap[channelName.ToUpper()].ReadProtectionStatus();
}
}
/// <summary>
/// Reads the mode of the Eload
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>The mode</returns>
public EloadModuleMode ReadMode(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
EloadModuleMode value = _eloadChannelMap[channelName.ToUpper()].ReadMode();
return value;
}
}
/// <summary>
/// Reads the overcurrent setting from an Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>Overcurrent setting (Amps).</returns>
public Current ReadOverCurrentProtection(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
var value = _eloadChannelMap[channelName.ToUpper()].ReadOverCurrentProtection();
return value;
}
}
/// <summary>
/// Reads the overvoltage setting from an Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>Overvoltage setting (Volts).</returns>
public Voltage ReadOverVoltageProtection(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
var value = _eloadChannelMap[channelName.ToUpper()].ReadOverVoltageProtection();
return value;
}
}
/// <summary>
/// Read the resistance.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>The resistance (Ohms).</returns>
public Resistance ReadResistance(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
var value = _eloadChannelMap[channelName.ToUpper()].ReadResistance();
return value;
}
}
/// <summary>
/// Reads the setpoint of the Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>The setpoint. Depends on mode.</returns>
public double ReadSetpoint(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
return _eloadChannelMap[channelName.ToUpper()].ReadSetpoint();
}
}
/// <summary>
/// Reads the voltage of the Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>The voltage (Volts)</returns>
public Voltage ReadVoltage(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
var value = _eloadChannelMap[channelName.ToUpper()].ReadVoltage();
return value;
}
}
/// <summary>
/// Reads the protection status from an Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <returns>Protection status register.</returns>
public ushort ReadProtectionStatus(string channelName)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
ushort value = _eloadChannelMap[channelName.ToUpper()].ReadProtectionStatus();
return value;
}
}
/// <summary>
/// Run a self-test.
/// </summary>
public void Selftest()
{
try
{
// change the timeout to account for the long self test to 30 seconds
if (_interface == ControlInterface.COM_PORT)
{
//@@@ is this the right time?
_serialPort.ReadTimeout = 30000;
}
else if (_interface == ControlInterface.GPIB)
{
//@@@ TBD
}
else
{
throw new Exception("unknown interface type: " + _interface.ToString());
}
// send the command
string rspStr = IOQuery(_SELFTEST);
// parse the response
string[] tokens = rspStr.Split('\n');
int rsp = Util.ConvertStringToInt32(tokens[0]);
if (rsp != 0)
{
string errorMsg = "returned an error: " + rsp.ToString();
throw new Exception(errorMsg);
}
}
catch (Exception)
{
throw;
}
finally
{
// restore the timeout
if (_interface == ControlInterface.COM_PORT)
{
_serialPort.ReadTimeout = _READ_TIMEOUT;
}
else if (_interface == ControlInterface.GPIB)
{
//@@@ TBD
}
else
{
throw new Exception("unknown interface type: " + _interface.ToString());
}
}
}
/// <summary>
/// Resets all channels to config file values
/// </summary>
public void SetInitialSetting(string module)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(module.ToUpper()) == false)
{
throw new Exception("could not find load: " + module.ToUpper());
}
_eloadChannelMap[module.ToUpper()].SetInitialSetting();
}
}
/// <summary>
/// Resets all channels to config file values
/// </summary>
public void SetInitialSettingAll()
{
lock (_syncObj)
{
foreach (KeyValuePair<String, IEload> eload in _eloadChannelMap)
{
_eloadChannelMap[eload.Key].SetInitialSetting();
}
}
}
/// <summary>
/// Change the operation mode of the Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <param name="mode">The desired Eload mode.</param>
public void SetMode(string channelName, EloadModuleMode mode)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
_eloadChannelMap[channelName.ToUpper()].SetMode(mode);
}
}
/// <summary>
/// Change the setpoint and operation mode of the Eload.
/// </summary>
/// <param name="channelName">The name of the Eload.</param>
/// <param name="newSetpoint">The desired setpoint of the Eload.</param>
/// <param name="mode">The desired Eload mode.</param>
public void SetSetpoint(string channelName, double newSetpoint, EloadModuleMode mode)
{
lock (_syncObj)
{
if (_eloadChannelMap.ContainsKey(channelName.ToUpper()) == false)
{
throw new Exception("could not find module: " + channelName.ToUpper());
}
_eloadChannelMap[channelName.ToUpper()].SetSetpoint(newSetpoint, mode);
}
}
/// <summary>
/// Dispose of this objects resources
/// </summary>
/// <param name="disposing">True = currently disposing, False = not disposing.</param>
protected virtual void Dispose(bool disposing)
{
lock (_syncObj)
{
if (disposing)
{
foreach (KeyValuePair<string, IEload> entry in _eloadChannelMap)
{
entry.Value.Shutdown();
}
if (_serialPort != null)
{
Reset();
_serialPort.Dispose();
_serialPort = null;
}
if (_gpibDevice != null)
{
Reset();
_gpibDevice.Dispose();
_gpibDevice = null;
}
}
}
}
public bool ClearErrors()
{
throw new NotImplementedException();
}
public void Initialize()
{
throw new NotImplementedException();
}
public SelfTestResult PerformSelfTest()
{
throw new NotImplementedException();
}
void IInstrument.Reset()
{
throw new NotImplementedException();
}
public void Shutdown()
{
throw new NotImplementedException();
}
#endregion
}
}