Files
GenericTeProgramLibrary/Source/TSRealLib/HAL/Implementations/DIO/DIOContec/DIOContec.cs
2025-03-13 12:04:22 -07:00

614 lines
17 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 CdioCs;
using Raytheon.Instruments.GeneralIO;
using System.Collections.Generic;
using NLog;
using Raytheon.Common;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
/// <summary>
/// A class that implements a Contec DIO card
/// </summary>
public class DIOContec : IGeneralIO, IDisposable
{
#region PrivateClassMembers
private Cdio _dio;
private short _id;
private string _name;
private string _address;
private SelfTestResult _selfTestResult;
private State _state;
private object _syncObj = new Object();
private int _numChannelPerPort = 8;
private int _channelStartIndex = 0;
private int _numInputChannels;
private int _numOutputChannels;
private bool _shallWeInitializeOutput = false;
private Dictionary<string, IODatatypes.DIOChannelInfo> _signalNameToChannelMap = new Dictionary<string, IODatatypes.DIOChannelInfo>();
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
#region PrivateClassFunctions
/// <summary>
/// Dispose of this object's resources
/// </summary>
/// <param name="disposing">True = currently disposing, False = not disposing.</param>
protected virtual void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_state == State.Ready)
{
Reset();
_dio.Exit(_id);
_state = State.Uninitialized;
}
}
}
catch (Exception)
{
try
{
//ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
}
catch (Exception)
{
//Do not rethrow. Exception from error logger that has already been garbage collected.
}
}
}
/// <summary>
/// Invert Contec DIO bits.
/// </summary>
private byte InvertBit(byte bitValue)
{
if (bitValue == 0)
{
return 1;
}
else if (bitValue == 1)
{
return 0;
}
else
{
throw new Exception("Unexpected input value: " + bitValue.ToString());
}
}
#endregion
#region PublicClassFunctions
/// <summary>
/// DIOContec factory constructor
/// </summary>
/// <param name="deviceName"></param>
/// <param name="configurationManager"></param>
public DIOContec(string deviceName, IConfigurationManager configurationManager, ILogger logger)
{
Name = deviceName;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
_address = _configuration.GetConfigurationValue("DIOContec", "Address", "");
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string dioModuleDefPath = _configuration.GetConfigurationValue(deviceName, ConfigXml.DIO_MODULE_DEF_FILEPATH.ToString());
if (!Path.IsPathRooted(dioModuleDefPath))
dioModuleDefPath = Path.GetFullPath(Path.Combine(assemblyFolder, dioModuleDefPath));
IConfigurationFile dioModuleConfig = new ConfigurationFile(dioModuleDefPath);
Boolean.TryParse(dioModuleConfig.ReadValue(Name, ConfigIni.SHALL_WE_DRIVE_OUTPUT_UPON_INITIALIZATION.ToString()), out _shallWeInitializeOutput);
_address = dioModuleConfig.ReadValue(Name, ConfigIni.DIO_ADDRESS.ToString());
Int32.TryParse(dioModuleConfig.ReadValue(Name, ConfigIni.NUM_INPUT_CHANNELS.ToString()), out _numInputChannels);
Int32.TryParse(dioModuleConfig.ReadValue(Name, ConfigIni.NUM_OUTPUT_CHANNELS.ToString()), out _numOutputChannels);
Int32.TryParse(dioModuleConfig.ReadValue(Name, ConfigIni.NUM_CHANNELS_PER_PORT.ToString()), out _numChannelPerPort);
Int32.TryParse(dioModuleConfig.ReadValue(Name, ConfigIni.CHANNEL_START_INDEX.ToString()), out _channelStartIndex);
if (!(_channelStartIndex == 0 || _channelStartIndex == 1))
{
throw new Exception($"The value for key {ConfigIni.CHANNEL_START_INDEX.ToString()} in section {Name} must be 0 or 1 in {dioModuleDefPath}");
}
List<string> outputSignalNames = dioModuleConfig.ReadAllKeys($"{Name}.{ConfigIni.OUTPUT_SIGNALS}");
List<string> intputSignalNames = dioModuleConfig.ReadAllKeys($"{Name}.{ConfigIni.INPUT_SIGNALS}");
IODatatypes.DIOChannelInfo info;
foreach (string signalName in outputSignalNames)
{
if (_signalNameToChannelMap.ContainsKey(signalName))
throw new Exception($"Key {signalName} in section {Name}.{ConfigIni.OUTPUT_SIGNALS} conflicts with the same key defined in another section.");
string iniLine = dioModuleConfig.ReadValue($"{Name}.{ConfigIni.OUTPUT_SIGNALS}", signalName);
string[] infoTokens = iniLine.Split('|');
if (infoTokens.Length != 2)
{
throw new Exception($"Key {signalName} in section {Name}.{ConfigIni.OUTPUT_SIGNALS} does not contain 2 tokens");
}
info.channelNumber = Convert.ToUInt32(infoTokens[0]);
info.initialValue = Convert.ToInt32(infoTokens[1]);
_signalNameToChannelMap[signalName] = info;
}
foreach (string signalName in intputSignalNames)
{
if (_signalNameToChannelMap.ContainsKey(signalName))
throw new Exception($"Key {signalName} in section {Name}.{ConfigIni.INPUT_SIGNALS} conflicts with the same key defined in another section.");
string iniLine = dioModuleConfig.ReadValue($"{Name}.{ConfigIni.INPUT_SIGNALS}", signalName);
info.channelNumber = Convert.ToUInt32(iniLine);
info.initialValue = -1;
_signalNameToChannelMap[signalName] = info;
}
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
// set in Initialize()
_dio = null;
_id = -1;
}
/// <summary>
/// Constructor which opens a handle to the DIO instrument and resets it.
/// Note that all signals on the Contec DIO card are inverted.
/// This class masks the inversion so that the caller does not have to deal with it.
/// </summary>
public DIOContec(string name, string address, List<uint> inputPins, List<uint> outputPins)
{
_name = name;
_address = address;
_logger = LogManager.GetCurrentClassLogger();
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
// set in Initialize()
_dio = null;
_id = -1;
}
/// <summary>
/// The finalizer.
/// </summary>
~DIOContec()
{
Dispose(false);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool ClearErrors()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public string DetailedStatus
{
get
{
return "This is a Contec DIO Card " + _name;
}
}
/// <summary>
///
/// </summary>
public bool DisplayEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
/// Dispose of this object's resources.
/// </summary>
public void Dispose()
{
lock (_syncObj)
{
try
{
Dispose(true);
GC.SuppressFinalize(this);
}
catch (Exception)
{
try
{
//ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
}
catch (Exception)
{
//Do not rethrow. Exception from error logger that has already been garbage collected.
}
}
}
}
/// <summary>
///
/// </summary>
public bool FrontPanelEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
/// <param name="signalName"></param>
/// <returns></returns>
public IODatatypes.BitState GetBitState(string signalName)
{
lock (_syncObj)
{
if (!_signalNameToChannelMap.ContainsKey(signalName))
throw new Exception($"Signal name {signalName} doesn't exist for card: " + _name);
if (_signalNameToChannelMap[signalName].channelNumber >= _numInputChannels || _signalNameToChannelMap[signalName].channelNumber < _channelStartIndex)
{
throw new Exception($"The input channel number {_signalNameToChannelMap[signalName].channelNumber} specified must be >= {_channelStartIndex} and < {_numInputChannels + _channelStartIndex} on card " + _name);
}
int bitIndex = (int)_signalNameToChannelMap[signalName].channelNumber - _channelStartIndex;
byte dioData = 0;
int ret = _dio.InpBit(_id, (short)bitIndex, out dioData);
if (ret != 0)
{
string errorString = "";
_dio.GetErrorString(ret, out errorString);
throw new Exception("call to InpBit returned error: " + ret.ToString() + ", " + errorString + " on card " + _name);
}
return (IODatatypes.BitState)InvertBit(dioData);
}
}
/// <summary>
/// Get state of an output signal. The contec DIO states are inverted.
/// This function masks the inversion before returning.
/// </summary>
/// <param name="bitno">The bit number of the signal.</param>
/// <returns>The state of the signal. 0 for low, 1 for high.</returns>
/*public DioMeasurementInstruments.SignalState GetOutputSignalState(uint bitno)
{
byte dioData = 0;
int ret = _dio.EchoBackBit(_id, (short)bitno, out dioData);
if (ret != 0)
{
string errorString = "";
_dio.GetErrorString(ret, out errorString);
throw new Exception("call to EchoBackBit returned error: " + ret.ToString() + ", " + errorString);
}
dioData = InvertBit(dioData);
if (dioData == 0)
{
return DioMeasurementInstruments.SignalState.LOW;
}
else
{
return DioMeasurementInstruments.SignalState.HIGH;
}
}*/
/// <summary>
///
/// </summary>
public InstrumentMetadata Info
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public void Initialize()
{
lock (_syncObj)
{
if (_state == State.Uninitialized)
{
_dio = new Cdio();
int ret = _dio.Init(_address, out _id);
if (ret != 0)
{
string errorString = "";
_dio.GetErrorString(ret, out errorString);
throw new Exception("call to Init returned error: " + ret.ToString() + ", " + errorString + " on card " + _name);
}
Reset();
if (_shallWeInitializeOutput)
{
foreach (KeyValuePair<string, IODatatypes.DIOChannelInfo> item in _signalNameToChannelMap)
{
if (item.Value.initialValue != -1)
{
SetBit(item.Key, (IODatatypes.BitState)item.Value.initialValue);
}
}
}
_state = State.Ready;
}
else
{
throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString() + " on card " + _name);
}
}
}
/// <summary>
/// Return list of signal names
/// </summary>
public List<string> GetSignalNames()
{
return new List<string>(_signalNameToChannelMap.Keys);
}
/// <summary>
///
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
///
/// </summary>
public uint NumberOfInputBits
{
get
{
return (uint)_numInputChannels;
}
}
/// <summary>
///
/// </summary>
public uint NumberOfOutputBits
{
get
{
return (uint)_numOutputChannels;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public SelfTestResult PerformSelfTest()
{
// card does not support self test
throw new NotImplementedException("card does not support self test" + " on card " + _name);
}
/// <summary>
///
/// </summary>
public void Reset()
{
lock (_syncObj)
{
int ret = _dio.ResetDevice(_id);
if (ret != 0)
{
string errorString = "";
_dio.GetErrorString(ret, out errorString);
throw new Exception("call to ResetDevice returned error: " + ret.ToString() + ", " + errorString + " on card " + _name);
}
}
}
/// <summary>
///
/// </summary>
public SelfTestResult SelfTestResult
{
get
{
return _selfTestResult;
}
}
/// <summary>
///
/// </summary>
/// <param name="signalName"></param>
/// <param name="state"> high(open) or low(closed) </param>
///
public void SetBit(string signalName, IODatatypes.BitState state)
{
lock (_syncObj)
{
if (!_signalNameToChannelMap.ContainsKey(signalName))
throw new Exception($"Signal name {signalName} doesn't exist for card: " + _name);
if (_signalNameToChannelMap[signalName].channelNumber >= _numOutputChannels || _signalNameToChannelMap[signalName].channelNumber < _channelStartIndex)
{
throw new Exception($"The output channel number {_signalNameToChannelMap[signalName].channelNumber} specified must be >= {_channelStartIndex} and < {_numOutputChannels + _channelStartIndex} on card " + _name);
}
int bitIndex = (int)_signalNameToChannelMap[signalName].channelNumber - _channelStartIndex;
int ret = _dio.OutBit(_id, (short)bitIndex, InvertBit((byte)state));
if (ret != 0)
{
string errorString = "";
_dio.GetErrorString(ret, out errorString);
throw new Exception("call to OutBit returned error: " + ret.ToString() + ", " + errorString + " on card " + _name);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="bit"></param>
public void SetTristate(string signalName)
{
lock (_syncObj)
{
}
}
/// <summary>
///
/// </summary>
/// <param name="bit"></param>
/// <param name="frequencyInHz"></param>
/// <param name="dutyCylePercentage"></param>
public void StartClock(uint bit, double frequencyInHz, double dutyCylePercentage)
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
/// <param name="bit"></param>
public void StopClock(uint bit)
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public State Status
{
get
{
return _state;
}
}
/// <summary>
///
/// </summary>
public void Shutdown()
{
lock (_syncObj)
{
if (_state == State.Ready)
{
Reset();
_dio.Exit(_id);
_state = State.Uninitialized;
}
}
}
#endregion
}
}