Big changes

This commit is contained in:
Duc
2025-03-13 12:04:22 -07:00
parent c689fcb7f9
commit ffa9905494
748 changed files with 199255 additions and 3743 deletions

View File

@@ -0,0 +1,360 @@
// Ignore Spelling: Geu Sdlc
using System;
using Raytheon.GuidedElectronicsUnit;
using System.Threading;
using Raytheon.Common;
using NLog;
namespace Raytheon.Instruments
{
public class CommDeviceGeuSdlc : ICommDevice, IDisposable
{
#region PrivateClassMembers
private GuidedElectronicsUnit.GuidedElectronicsUnit _guidanceElectronicsUnit;
private readonly bool _idQuery = false;
private readonly bool _reset = false;
private readonly SelfTestResult _selfTestResult;
private State _state;
private readonly string _instrumentDriverSetup;
private readonly int _pollingRate;
private readonly string _resourceName;
private static readonly object _syncObj = new object();
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
#region PrivatelassFunctions
/// <summary>
/// The Finalizer
/// </summary>
~CommDeviceGeuSdlc()
{
Dispose(false);
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close resources
try
{
if (_state == State.Ready)
{
_guidanceElectronicsUnit.Close();
_guidanceElectronicsUnit.Dispose();
_state = State.Uninitialized;
}
}
catch (Exception)
{
try
{
}
catch (Exception)
{
//Do not rethrow. Exception from error logger that has already been garbage collected
}
}
}
}
/// <summary>
/// Some HSS corrections
/// </summary>
/// <param name="data"></param>
/// <param name="numDWords"></param>
private static unsafe void PerformHssSwirl(ref byte[] data, uint numDWords)
{
fixed (byte* pBytePtr = &data[0])
{
for (int i = 0; i < numDWords; i++)
{
// swap the first word
ushort* pWord1 = (ushort*)pBytePtr + (i * 2);
*pWord1 = Util.Swap(*pWord1);
//swap the second word
ushort* pWord2 = (ushort*)pBytePtr + ((i * 2) + 1);
*pWord2 = Util.Swap(*pWord2);
// now swap the dword
uint* pDWord = (uint*)pBytePtr + i;
*pDWord = Util.SwapHighAndLowBytes(*pDWord);
}
}
}
#endregion
#region PublicClassFunctions
bool IInstrument.DisplayEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
bool IInstrument.FrontPanelEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
InstrumentMetadata IInstrument.Info => throw new NotImplementedException();
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="deviceName"></param>
/// <param name="configurationManager"></param>
public CommDeviceGeuSdlc(string deviceName, IConfigurationManager configurationManager, ILogger logger)
{
_resourceName = deviceName;
_guidanceElectronicsUnit = null;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
_idQuery = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "IdQuery", true);
_reset = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "Reset", true);
_instrumentDriverSetup = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "InstrumentDriverSetup", "");
_pollingRate = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "PollingRate", 10);
}
/// <summary>
/// The constructor. Does not initialize anything. Use Initialize() to create the handle to the hardware
/// </summary>
/// <param name="resourceName"></param>
/// <param name="idQuery"></param>
/// <param name="reset"></param>
public CommDeviceGeuSdlc(string resourceName, bool idQuery, bool reset, string instrumentDriverSetup = "", int pollingRate = 10)
{
_resourceName = resourceName;
_idQuery = idQuery;
_reset = reset;
_guidanceElectronicsUnit = null;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_instrumentDriverSetup = instrumentDriverSetup;
_pollingRate = pollingRate;
_logger = LogManager.GetCurrentClassLogger();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IInstrument.ClearErrors()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public string DetailedStatus
{
get
{
return "This is a HSS GEU SDLC Device called " + _resourceName;
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
try
{
Dispose(true);
GC.SuppressFinalize(this);
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
/// <summary>
/// Create a handle to the hardware
/// </summary>
public void Initialize()
{
const uint AUTO_FWDING_ADDR = 0x00080100;
const uint WRITE_OFFSET = 0x00200000;
const uint ENABLE_AUTO_FWDING = 0b0000_0001;
const uint ENABLE_TLP_HDR = 0b0000_0010;
//const uint INSERT_MSG_CNT = 0b0000_0100;
_guidanceElectronicsUnit = new GuidedElectronicsUnit.GuidedElectronicsUnit(_resourceName, _idQuery, _reset,
$"QueryInstrStatus=true, Simulate=false, DriverSetup= {_instrumentDriverSetup}, PollingInterval={_pollingRate}");
_guidanceElectronicsUnit.LowLevel.HSSub9100.EnableIO = ControlState.Enabled;
_guidanceElectronicsUnit.LowLevel.HSSub9100.WriteRegister(AUTO_FWDING_ADDR + WRITE_OFFSET, ENABLE_AUTO_FWDING | ENABLE_TLP_HDR);
_state = State.Ready;
}
/// <summary>
///
/// </summary>
public string Name
{
get
{
return _resourceName;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
SelfTestResult IInstrument.PerformSelfTest()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
/// <param name="dataRead"></param>
/// <returns></returns>
uint ICommDevice.Read(ref byte[] dataRead)
{
if (_guidanceElectronicsUnit == null)
{
return 0;
}
byte[] sdlcMsgs = new byte[0];
lock (_syncObj)
{
// read all of the data that is available
sdlcMsgs = _guidanceElectronicsUnit.GsKwSyncronousDataLinkControl.FetchMessageData();
}
if (sdlcMsgs.Length > dataRead.Length)
{
throw new Exception("The data buffer that the host provided is: " + dataRead.Length + " bytes, there are: " + sdlcMsgs.Length + " bytes of SDLC data. Need to increase the host buffer size");
}
Buffer.BlockCopy(sdlcMsgs, 0, dataRead, 0, sdlcMsgs.Length);
return (uint)sdlcMsgs.Length;
}
/// <summary>
///
/// </summary>
void IInstrument.Reset()
{
lock (_syncObj)
{
_guidanceElectronicsUnit.Close();
_state = State.Uninitialized;
Thread.Sleep(500);
Initialize();
}
}
/// <summary>
///
/// </summary>
public SelfTestResult SelfTestResult
{
get
{
return _selfTestResult;
}
}
/// <summary>
///
/// </summary>
/// <param name="timeoutMs"></param>
void ICommDevice.SetReadTimeout(uint timeoutMs)
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
void IInstrument.Shutdown()
{
lock (_syncObj)
{
if (_guidanceElectronicsUnit != null)
{
_guidanceElectronicsUnit.LowLevel.HSSub9100.EnableIO = ControlState.Disabled;
_guidanceElectronicsUnit.Close();
_guidanceElectronicsUnit.Dispose();
_state = State.Uninitialized;
}
}
}
/// <summary>
///
/// </summary>
public State Status
{
get
{
return _state;
}
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
uint ICommDevice.Write(byte[] data, uint numBytesToWrite)
{
lock (_syncObj)
{
if (numBytesToWrite % 4 != 0)
{
throw new Exception("Data is not dword aligned");
}
// do all of the HSS Tx only byte order corrections
PerformHssSwirl(ref data, numBytesToWrite / 4);
var tempArr = new uint[data.Length / 4];
Buffer.BlockCopy(data, 0, tempArr, 0, (int)numBytesToWrite);
_guidanceElectronicsUnit.GsKwSyncronousDataLinkControl.SendMessage(tempArr);
}
return numBytesToWrite;
}
public void Close()
{
}
public void Open()
{
}
#endregion
}
}

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<AssemblyName>Raytheon.Instruments.CommDeviceGeuSdlc</AssemblyName>
<Product>CommDevice GeuSdlc implementation</Product>
<Description>CommDevice GeuSdlc implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<Version>1.0.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.0" />
<PackageReference Include="Raytheon.Common" Version="1.0.0" />
<PackageReference Include="Raytheon.Instruments.CommDevice.Contracts" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommDeviceSim\CommDeviceSim.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="netfscc">
<HintPath>..\..\Common\COTS\FSCC\netfscc.dll</HintPath>
</Reference>
<Reference Include="Raytheon.GuidedElectronicsUnit.Fx46">
<HintPath>..\..\Common\COTS\Teradyne_SDLC\Raytheon.GuidedElectronicsUnit.Fx46.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,141 @@
// **********************************************************************************************************
// CommDeviceGeuSdlcFactory.cs
// 2/20/2023
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
// Ignore Spelling: Sdlc Geu
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceGeuSdlcFactory")]
public class CommDeviceGeuSdlcFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceGeuSdlcFactory(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 CommDeviceGeuSdlcFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null )
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommDevice));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceGeuSdlc(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
if (simulateHw)
return new CommDeviceSim(name, _configurationManager, _logger);
else
return new CommDeviceGeuSdlc(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,416 @@
// **********************************************************************************************************
// CommDeviceSerialAsync.cs
// 4/3/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Reflection;
using System.IO.Ports;
namespace Raytheon.Instruments
{
/// <summary>
/// A sim communication device
/// </summary>
public class CommDeviceSerialAsync : ICommAsync
{
#region PrivateClassMembers
private uint _defaultReadTimeout;
private uint _defaultSendTimeout;
private uint _defaultReadBufferSize;
private static readonly object _syncObj = new object();
private SerialPort _serialPort;
private readonly string _comPortName;
private readonly int _baudRate;
private readonly Parity _parity;
private readonly int _dataBits;
private readonly StopBits _stopBits;
private readonly string _name;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
public bool ClearErrors() => false;
public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); }
public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); }
public string DetailedStatus => $"This is a TCP/IP Device called {_name}";
public InstrumentMetadata Info => throw new NotImplementedException();
public State Status => _state;
public string Name => _name;
public SelfTestResult PerformSelfTest() => SelfTestResult;
public SelfTestResult SelfTestResult => SelfTestResult.Unknown;
public void Close() => Shutdown();
public void Reset()
{
Close();
Open();
}
#region Private Functions
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
try
{
lock (_syncObj)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close the socket
try
{
Shutdown();
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
}
#endregion
#region Public Functions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceSerialAsync(string name, IConfigurationManager configurationManager, ILogger logger)
{
_name = name;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(name);
_logger = logger;
_comPortName = _configuration.GetConfigurationValue("CommDeviceSerialAsync", "COMPortName", "COM15");
_baudRate = _configuration.GetConfigurationValue("CommDeviceSerialAsync", "BaudRate", 115200);
_parity = _configuration.GetConfigurationValue("CommDeviceSerialAsync", "Parity", Parity.None);
_dataBits = _configuration.GetConfigurationValue("CommDeviceSerialAsync", "DataBits", 8);
_stopBits = _configuration.GetConfigurationValue("CommDeviceSerialAsync", "StopBits", StopBits.One);
_defaultReadTimeout = _configuration.GetConfigurationValue<uint>("CommDeviceSerialAsync", "ReadTimeout", 25);
_defaultSendTimeout = _configuration.GetConfigurationValue<uint>("CommDeviceSerialAsync", "SendTimeout", 5000);
_defaultReadBufferSize = _configuration.GetConfigurationValue<uint>("CommDeviceSerialAsync", "BufferSize", 1024);
_state = State.Uninitialized;
}
/// <summary>
/// initialize instrument
/// </summary>
public void Initialize()
{
if (_state != State.Uninitialized)
{
_logger.Warn("Reinitialization of existing Serial Async Connection. Attempting to call Shutdown.");
Shutdown();
}
_serialPort = new SerialPort(_comPortName, _baudRate, _parity, _dataBits, _stopBits);
Open();
}
/// <summary>
/// Opens COM serial port for communications
/// </summary>
public void Open()
{
try
{
_serialPort.Open();
_state = State.Ready;
}
catch (Exception ex)
{
_logger.Error(ex, ex.Message);
throw;
}
}
/// <summary>
/// shuts down the device
/// </summary>
public void Shutdown()
{
_logger.Debug("Shutting down");
_serialPort.Close();
_state = State.Uninitialized;
}
/// <summary>
/// Read data from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<uint> ReadAsync(byte[] dataRead, CancellationToken token = default)
{
var bytesRead = await _serialPort.BaseStream.ReadAsync(dataRead, 0, dataRead.Length, token);
return (uint)bytesRead;
}
/// <summary>
/// Read string from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<string> ReadAsync(CancellationToken token = default)
{
var data = await ReadLineAsync(token);
return data;
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
if (_serialPort == null)
return;
_logger.Trace($"Setting Reader Timeout: {timeoutMs} Ms");
_serialPort.ReadTimeout = (int)timeoutMs;
}
/// <summary>
/// Write data to the device asynchronously
/// </summary>
/// <param name="dataToSend"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
public async Task<uint> WriteAsync(byte[] dataToSend, uint numBytesToWrite, CancellationToken token = default)
{
if (_serialPort == null || !_serialPort.IsOpen)
return 0;
_logger.Trace($"Writing message to ({_comPortName}), bytes: {dataToSend?.Length}");
await _serialPort.BaseStream.WriteAsync(dataToSend, 0, (int)numBytesToWrite, token);
return numBytesToWrite;
}
/// <summary>
/// Write string data to the device asynchronously
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task WriteAsync(string message, CancellationToken token = default)
{
if (_serialPort == null || !_serialPort.IsOpen)
return;
_logger.Trace($"Writing message to ({_comPortName}), message: {message}");
await WriteLineAsync(message, token);
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<string> SendCommandGetResponseAsync(string message, CancellationToken cancellationToken = default, int timeoutInMs = 5000)
{
if (_serialPort == null || !_serialPort.IsOpen)
return null;
_logger.Trace($"Sending command waiting for response from ({_comPortName}), message: {message}");
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs)))
{
if (cancellationToken == default)
{
cancellationToken = cts.Token;
}
await WriteAsync(message, cancellationToken);
string readResponse = await ReadAsync(cancellationToken);
_logger.Trace($"Received response: {readResponse}");
return readResponse;
}
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<byte[]> SendCommandGetResponseAsync(byte[] data, CancellationToken token = default, int timeoutInMs = 5000)
{
if (_serialPort == null || !_serialPort.IsOpen)
return null;
_logger.Trace($"Sending command waiting for response from ({_comPortName}), message: {data}");
await WriteAsync(data, (uint)data.Length, token);
_serialPort.ReadTimeout = timeoutInMs;
var response = new byte[data.Length];
await ReadAsync(response, token);
return response;
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<string> dataReceived)
{
if (_serialPort == null || !_serialPort.IsOpen)
return;
_logger.Debug($"Starting continuous reading from {_comPortName} ...");
while (!cancellationToken.IsCancellationRequested)
{
var data = await ReadAsync(cancellationToken);
dataReceived?.Invoke(data);
}
_logger.Debug($"Finished continuous reading from {_comPortName} ...");
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<byte[]> dataReceived)
{
if (_serialPort == null || !_serialPort.IsOpen)
return;
_logger.Debug($"Starting continuous reading from {_comPortName} ...");
while (!cancellationToken.IsCancellationRequested)
{
var data = new byte[_defaultReadBufferSize]; // Adjust buffer size as needed
var bytesRead = await ReadAsync(data, cancellationToken);
Array.Resize(ref data, (int)bytesRead);
dataReceived?.Invoke(data);
}
_logger.Debug($"Finished continuous reading from {_comPortName} ...");
}
#endregion
#region private functions
/// <summary>
/// reads line async
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task<string> ReadLineAsync(CancellationToken cancellationToken = default)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
var line = await Task.Run(() => _serialPort.ReadLine(), cancellationToken);
return line;
}
catch (OperationCanceledException ex)
{
_logger.Error(ex, ex.Message);
return null;
}
}
/// <summary>
/// writes line async
/// </summary>
/// <param name="message"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task WriteLineAsync(string message, CancellationToken cancellationToken = default)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Run(() => _serialPort.WriteLine(message), cancellationToken);
}
catch (OperationCanceledException ex)
{
_logger.Error(ex, ex.Message);
}
}
#endregion
}
}

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceSerialAsync</AssemblyName>
<Product>CommDevice Serial Asynchronous implementation</Product>
<Description>CommDevice Serial COM Asynchronous implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommAsync.Contracts" Version="1.4.0" />
</ItemGroup>
<!-- Copy all *.dlls and *.pdb in the output folder to a temp folder -->
<Target Name="CopyFiles" AfterTargets="AfterBuild">
<ItemGroup>
<FILES_1 Include="$(OutDir)*.dll" />
<FILES_2 Include="$(OutDir)*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(FILES_1)" DestinationFolder="$(HalTempFolder)" />
<Copy SourceFiles="@(FILES_2)" DestinationFolder="$(HalTempFolder)" />
</Target>
</Project>

View File

@@ -0,0 +1,136 @@
// **********************************************************************************************************
// CommDeviceSerialAsyncFactory.cs
// 4/3/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceSerialAsyncFactory")]
public class CommDeviceSerialAsyncFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceSerialAsyncFactory(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 CommDeviceSerialAsyncFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommAsync));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceSerialAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceSerialAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,315 @@
// 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;
namespace Raytheon.Instruments
{
/// <summary>
/// A sim communication device
/// </summary>
public class CommDeviceSim : ICommDevice
{
#region PrivateClassMembers
private static object _syncObj = new Object();
private readonly string _name;
private SelfTestResult _selfTestResult;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
#region PrivateFunctions
/// <summary>
/// The Finalizer
/// </summary>
~CommDeviceSim()
{
Dispose(false);
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
}
#endregion
#region PublicFuctions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceSim(string name, IConfigurationManager configurationManager, ILogger logger)
{
_name = name;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
public CommDeviceSim(string name)
{
_name = name;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_logger = LogManager.GetCurrentClassLogger();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool ClearErrors()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public bool DisplayEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public string DetailedStatus
{
get
{
return "This is a Comm Device Sim called " + _name;
}
}
/// <summary>
/// Dispose of the resources
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
///
/// </summary>
public bool FrontPanelEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public InstrumentMetadata Info
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public void Initialize()
{
lock (_syncObj)
{
if (_state == State.Uninitialized)
{
_state = State.Ready;
}
else
{
throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString() + " on device " + _name);
}
}
}
/// <summary>
///
/// </summary>
public string Name
{
get
{
return _name;
}
}
/// <summary>
///
/// </summary>
/*public void Open()
{
lock (_syncObj)
{
}
}*/
/// <summary>
///
/// </summary>
/// <returns></returns>
public SelfTestResult PerformSelfTest()
{
lock (_syncObj)
{
_selfTestResult = SelfTestResult.Pass;
return _selfTestResult;
}
}
/// <summary>
/// Read data from the device.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public uint Read(ref byte[] dataRead)
{
lock (_syncObj)
{
return (uint)dataRead.Length;
}
}
/// <summary>
///
/// </summary>
public void Reset()
{
lock (_syncObj)
{
}
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeout"></param>
public void SetReadTimeout(uint timeout)
{
lock (_syncObj)
{
}
}
/// <summary>
///
/// </summary>
public SelfTestResult SelfTestResult
{
get
{
return _selfTestResult;
}
}
/// <summary>
///
/// </summary>
public State Status
{
get
{
return _state;
}
}
/// <summary>
///
/// </summary>
public void Shutdown()
{
lock (_syncObj)
{
_state = State.Uninitialized;
}
}
/// <summary>
/// Write data to the device
/// </summary>
/// <param name="dataToSend">The data to write</param>
/// <param name="numBytesToWrite">The number of bytes to write</param>
/// <returns>THe number of bytes that were written</returns>
public uint Write(byte[] dataToSend, uint numBytesToWrite)
{
lock (_syncObj)
{
return numBytesToWrite;
}
}
public void Close()
{
throw new NotImplementedException();
}
public void Open()
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceSim</AssemblyName>
<Product>CommDevice SIM implementation</Product>
<Description>CommDevice SIM implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommDevice.Contracts" Version="1.2.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,104 @@
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceSimFactory")]
public class CommDeviceSimFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceSimFactory(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 CommDeviceSimFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommDevice));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceSim(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceSim(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,517 @@
// 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;
namespace Raytheon.Instruments
{
/// <summary>
/// Class for controlling a Commtech SUPERFSCC/4-PCIE-11
/// </summary>
public class CommDeviceSuperFscc422 : ICommDevice, IDisposable
{
#region PrivateClassMembers
private const uint _DEFAULT_READ_TIMEOUT = 10;
private const uint _RX_FIFO_BUFFER_SIZE = 8192;
private const uint _TX_FIFO_BUFFER_SIZE = 4096;
// The Super FSCC can DMA can stream data at 50 Mbits/sec
// The driver automatically transfers data from the FIFO into the MemoryCap which has a configurable size. Default to ~GB
private uint _readTimeout;
private Fscc.Port _fscc;
private static object _syncObj = new Object();
private readonly string _name;
private SelfTestResult _selfTestResult;
private State _state;
private readonly uint _portNum;
private readonly uint _clockFreq;
private readonly bool _shallWeReceiveMultiple;
private readonly bool _shallWeAppendStatus;
private readonly uint _bgrRegister;
private readonly uint _ccr0Register;
private readonly uint _ccr0SofResetValue;
private readonly uint _ccr1Register;
private readonly uint _ccr2Register;
private readonly uint _dpllrRegister;
private readonly uint _fcrRegister;
private readonly uint _fifotRegister;
private readonly uint _imrRegister;
private readonly uint _pprRegister;
private readonly uint _ramrRegister;
private readonly uint _rarRegister;
private readonly uint _smrRegister;
private readonly uint _ssrRegister;
private readonly uint _tcrRegister;
private readonly uint _tmrRegister;
private readonly uint _tsrRegister;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
#region PrivateFuctions
/// <summary>
/// The Finalizer
/// </summary>
~CommDeviceSuperFscc422()
{
Dispose(false);
}
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_state == State.Ready)
{
_fscc.Dispose();
_state = State.Uninitialized;
}
}
}
catch (Exception err)
{
try
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
catch (Exception)
{
//Do not rethrow. Exception from error logger that has already been garbage collected
}
}
}
#endregion
#region PublicFuctions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceSuperFscc422(string name, IConfigurationManager configurationManager, ILogger logger)
{
_name = name;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
_portNum = _configuration.GetConfigurationValue<uint>("SuperFscc422", "PortNum", 0);
_clockFreq = _configuration.GetConfigurationValue<uint>("SuperFscc422", "ClockFreq", 0);
_shallWeReceiveMultiple = _configuration.GetConfigurationValue("SuperFscc422", "ShallWeReceiveMultiple", false);
_shallWeAppendStatus = _configuration.GetConfigurationValue("SuperFscc422", "ShallWeAppendStatus", false);
_bgrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "BgrRegister", 0);
_ccr0Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr0Register", 0);
_ccr0SofResetValue = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr0SofResetValue", 0);
_ccr1Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr1Register", 0);
_ccr2Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr2Register", 0);
_dpllrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "DpllrRegister", 0);
_fcrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "FcrRegister", 0);
_fifotRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "FifotRegister", 0);
_imrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "ImrRegister", 0);
_pprRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "PprRegister", 0);
_ramrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "RamrRegister", 0);
_rarRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "RarRegister", 0);
_smrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "SmrRegister", 0);
_ssrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "SsrRegister", 0);
_tcrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TcrRegister", 0);
_tmrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TmrRegister", 0);
_tsrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TsrRegister", 0);
_readTimeout = _DEFAULT_READ_TIMEOUT;
// created in Initialize()
_fscc = null;
}
/// <summary>
/// Opens the port and initializes the registers
/// </summary>
/// <param name="portNum"></param>
/// <param name="clockFreq"></param>
/// <param name="shallWeReceiveMultiple"></param>
/// <param name="shallWeAppendStatus"></param>
/// <param name="bgrRegister"></param>
/// <param name="ccr0Register"></param>
/// <param name="ccr0SofResetValue"></param>
/// <param name="ccr1Register"></param>
/// <param name="ccr2Register"></param>
/// <param name="dpllrRegister"></param>
/// <param name="fcrRegister"></param>
/// <param name="fifotRegister"></param>
/// <param name="imrRegister"></param>
/// <param name="pprRegister"></param>
/// <param name="ramrRegister"></param>
/// <param name="rarRegister"></param>
/// <param name="smrRegister"></param>
/// <param name="ssrRegister"></param>
/// <param name="tcrRegister"></param>
/// <param name="tmrRegister"></param>
/// <param name="tsrRegister"></param>
public CommDeviceSuperFscc422(string name, uint portNum, uint clockFreq, bool shallWeReceiveMultiple, bool shallWeAppendStatus, uint bgrRegister, uint ccr0Register,
uint ccr0SofResetValue, uint ccr1Register, uint ccr2Register, uint dpllrRegister, uint fcrRegister, uint fifotRegister,
uint imrRegister, uint pprRegister, uint ramrRegister, uint rarRegister, uint smrRegister, uint ssrRegister, uint tcrRegister, uint tmrRegister, uint tsrRegister)
{
_name = name;
_portNum = portNum;
_clockFreq = clockFreq;
_shallWeReceiveMultiple = shallWeReceiveMultiple;
_shallWeAppendStatus = shallWeAppendStatus;
_bgrRegister = bgrRegister;
_ccr0Register = ccr0Register;
_ccr0SofResetValue = ccr0SofResetValue;
_ccr1Register = ccr1Register;
_ccr2Register = ccr2Register;
_dpllrRegister = dpllrRegister;
_fcrRegister = fcrRegister;
_fifotRegister = fifotRegister;
_imrRegister = imrRegister;
_pprRegister = pprRegister;
_ramrRegister = ramrRegister;
_rarRegister = rarRegister;
_smrRegister = smrRegister;
_ssrRegister = ssrRegister;
_tcrRegister = tcrRegister;
_tmrRegister = tmrRegister;
_tsrRegister = tsrRegister;
_readTimeout = _DEFAULT_READ_TIMEOUT;
// created in Initialize()
_fscc = null;
_logger = LogManager.GetCurrentClassLogger();
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool ClearErrors()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public bool DisplayEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public string DetailedStatus
{
get
{
return "This is a FSCC 422 Device called " + _name;
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
lock (_syncObj)
{
try
{
Dispose(true);
GC.SuppressFinalize(this);
}
catch (Exception err)
{
try
{
_logger.Error(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>
public InstrumentMetadata Info
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public void Initialize()
{
lock (_syncObj)
{
if (_state == State.Uninitialized)
{
_fscc = new Fscc.Port(_portNum);
// false means that each read will return a single sdlc packet
// true means that each read will return everything in the buffer (multiple packets)
_fscc.RxMultiple = _shallWeReceiveMultiple;
// if this to true...extra 2 bytes arrive at the end of every frame (first two bytes of STAR)
// The class processing the data needs to be aware of the extra two bytes
_fscc.AppendStatus = _shallWeAppendStatus;
// ignore timeouts
_fscc.IgnoreTimeout = true;
// purge the port
_fscc.Purge(true, true);
// set the registers
_fscc.Registers.BGR = _bgrRegister;
_fscc.Registers.CCR0 = _ccr0Register;
_fscc.Registers.CCR1 = _ccr1Register;
_fscc.Registers.CCR2 = _ccr2Register;
_fscc.Registers.DPLLR = _dpllrRegister;
_fscc.Registers.FCR = _fcrRegister;
_fscc.Registers.FIFOT = _fifotRegister;
_fscc.Registers.IMR = _imrRegister;
_fscc.Registers.PPR = _pprRegister;
_fscc.Registers.RAMR = _ramrRegister;
_fscc.Registers.RAR = _rarRegister;
_fscc.Registers.SMR = _smrRegister;
_fscc.Registers.SSR = _ssrRegister;
_fscc.Registers.TCR = _tcrRegister;
_fscc.Registers.TMR = _tmrRegister;
_fscc.Registers.TSR = _tsrRegister;
// false means that each read will return a single sdlc packet
// true means that each read will return everything in the buffer (multiple packets)
_fscc.RxMultiple = _shallWeReceiveMultiple;
_fscc.ClockFrequency = _clockFreq;
// if this to true...extra 2 bytes arrive at the end of every frame (first two bytes of STAR)
// The class processing the data needs to be aware of the extra two bytes
_fscc.AppendStatus = _shallWeAppendStatus;
_state = State.Ready;
}
else
{
throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString() + " on device " + _name);
}
}
}
/// <summary>
///
/// </summary>
public string Name
{
get
{
return _name;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public SelfTestResult PerformSelfTest()
{
lock (_syncObj)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Read data from the device.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public uint Read(ref byte[] dataRead)
{
lock (_syncObj)
{
uint numBytesRead = _fscc.Read(dataRead, (uint)dataRead.Length, _readTimeout);
return numBytesRead;
}
}
/// <summary>
/// Soft reset procedure as suggested by the vendor
/// </summary>
public void Reset()
{
lock (_syncObj)
{
_fscc.Registers.CCR0 = _ccr0SofResetValue;
_fscc.Purge(true, true);
_fscc.Registers.CCR0 = _ccr0Register;
}
}
/// <summary>
///
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
lock (_syncObj)
{
_readTimeout = timeoutMs;
}
}
/// <summary>
///
/// </summary>
public SelfTestResult SelfTestResult
{
get
{
return _selfTestResult;
}
}
/// <summary>
///
/// </summary>
public State Status
{
get
{
return _state;
}
}
/// <summary>
///
/// </summary>
public void Shutdown()
{
lock (_syncObj)
{
if (_state == State.Ready)
{
_fscc.Dispose();
_state = State.Uninitialized;
}
}
}
/// <summary>
/// Write data to the device
/// </summary>
/// <param name="dataToSend">The data to write</param>
/// <param name="numBytesToWrite">The number of bytes to write</param>
/// <returns>THe number of bytes that were written</returns>
public uint Write(byte[] dataToSend, uint numBytesToWrite)
{
lock (_syncObj)
{
uint numWritten = _fscc.Write(dataToSend, numBytesToWrite);
if (numWritten != numBytesToWrite)
{
throw new Exception("num written " + numWritten + " not as expected: " + numBytesToWrite);
}
return numWritten;
}
}
public void Close()
{
throw new NotImplementedException();
}
public void Open()
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<AssemblyName>Raytheon.Instruments.CommDeviceSuperFscc422</AssemblyName>
<Product>CommDevice SuperFscc422 implementation</Product>
<Description>CommDevice SuperFscc422 implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.0" />
<PackageReference Include="Raytheon.Common" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommDeviceSim\CommDeviceSim.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="netfscc">
<HintPath>..\..\Common\COTS\FSCC\netfscc.dll</HintPath>
</Reference>
<Reference Include="Raytheon.GuidedElectronicsUnit.Fx46">
<HintPath>..\..\Common\COTS\Teradyne_SDLC\Raytheon.GuidedElectronicsUnit.Fx46.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,161 @@
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceSuperFscc422Factory")]
public class CommDeviceSuperFscc422Factory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceSuperFscc422Factory(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 CommDeviceSuperFscc422Factory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommDevice));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceSuperFscc422(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
if (simulateHw)
return new CommDeviceSim(name, _configurationManager, _logger);
else
return new CommDeviceSuperFscc422(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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);
}
/// <summary>
///
/// </summary>
/// <param name="instrumentDefFile"></param>
/// <param name="iniSectionName"></param>
/// <param name="isThereHardware"></param>
/// <returns></returns>
public static ICommDevice CreateFastCommDevice(string instrumentDefFile, string iniSectionName, bool isThereHardware)
{
const string FAST_COMM_FSCC = "FAST_COMM_FSCC";
IniFile iniReader = new IniFile(instrumentDefFile);
bool shallWeDebug = Convert.ToBoolean(iniReader.ReadValue(FAST_COMM_FSCC, "SHALL_WE_DEBUG"));
uint port = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "PORT"));
uint clock = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "CLOCK_FREQUENCY"), 16);
uint bgr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "BGR_REGISTER"), 16);
uint ccr0 = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "CCR0_REGISTER"), 16);
uint ccr0Reset = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "CCR0_SOF_RESET_VALUE"), 16);
uint ccr1 = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "CCR1_REGISTER"), 16);
uint ccr2 = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "CCR2_REGISTER"), 16);
uint dpllr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "DPLLR_REGISTER"), 16);
uint fcr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "FCR_REGISTER"), 16);
uint fifot = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "FIFO_T_REGISTER"), 16);
uint imr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "IMR_REGISTER"), 16);
uint ppr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "PPR_REGISTER"), 16);
uint ramr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "RAMR_REGISTER"), 16);
uint rar = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "RAR_REGISTER"), 16);
uint smr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "SMR_REGISTER"), 16);
uint ssr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "SSR_REGISTER"), 16);
uint tcr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "TCR_REGISTER"), 16);
uint tmr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "TMR_REGISTER"), 16);
uint tsr = Convert.ToUInt32(iniReader.ReadValue(FAST_COMM_FSCC, "TSR_REGISTER"), 16);
bool shallWeReceiveMultiple = true;
bool shallWeAppendStatus = false;
if (shallWeDebug == true)
{
shallWeReceiveMultiple = false;
shallWeAppendStatus = true;
}
if (isThereHardware == true)
{
return new CommDeviceSuperFscc422(iniSectionName, port, clock, shallWeReceiveMultiple, shallWeAppendStatus, bgr, ccr0, ccr0Reset, ccr1, ccr2, dpllr, fcr, fifot, imr, ppr, ramr, rar, smr, ssr, tcr, tmr, tsr);
}
else
{
return new CommDeviceSim(iniSectionName);
}
}
}
}

View File

@@ -0,0 +1,465 @@
// **********************************************************************************************************
// CommDeviceTcpAsync.cs
// 3/6/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Reflection;
namespace Raytheon.Instruments
{
/// <summary>
/// A sim communication device
/// </summary>
public class CommDeviceTcpAsync : ICommAsync
{
#region PrivateClassMembers
private uint _defaultReadTimeout;
private uint _defaultSendTimeout;
private uint _defaultReadBufferSize;
private static readonly object _syncObj = new object();
private TcpClient _tcpClient;
private TcpListener _tcpListener;
private NetworkStream _tcpIpStream;
private int _port;
private string _remoteAddress;
private int _remotePort;
private readonly string _name;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
public bool ClearErrors() => false;
public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); }
public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); }
public string DetailedStatus => $"This is a TCP/IP Device called {_name}";
public InstrumentMetadata Info => throw new NotImplementedException();
public State Status => _state;
public string Name => _name;
public SelfTestResult PerformSelfTest() => SelfTestResult;
public SelfTestResult SelfTestResult => SelfTestResult.Unknown;
public void Open() => Initialize();
public void Close() => Shutdown();
public void Reset()
{
Close();
Open();
}
#region Private Functions
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
try
{
lock (_syncObj)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close the socket
try
{
Shutdown();
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
}
#endregion
#region Public Functions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceTcpAsync(string name, IConfigurationManager configurationManager, ILogger logger)
{
_name = name;
// _tcpClient is created in Initialize()
_tcpClient = null;
_tcpIpStream = null;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
}
/// <summary>
/// initialize instrument
/// </summary>
public void Initialize()
{
if (_state != State.Uninitialized)
{
_logger.Warn("Reinitialization of existing TCP Async Connection. Attempting to call Shutdown.");
Shutdown();
}
_defaultReadTimeout = _configuration.GetConfigurationValue<uint>("TcpClient", "ReadTimeout", 25);
_defaultSendTimeout = _configuration.GetConfigurationValue<uint>("TcpClient", "SendTimeout", 5000);
_defaultReadBufferSize = _configuration.GetConfigurationValue<uint>("TcpClient", "BufferSize", 1024);
_remoteAddress = _configuration.GetConfigurationValue("TcpClient", "RemoteAddress", "127.0.0.1");
_port = _configuration.GetConfigurationValue("TcpClient", "Port", 0);
if (string.IsNullOrEmpty(_remoteAddress))
{
_tcpListener = new TcpListener(IPAddress.Any, _port);
_tcpListener.Start();
_logger.Debug($"{MethodBase.GetCurrentMethod().Name} Started Listening on Port: {_port}");
Task.Run(async () => await _tcpListener.AcceptTcpClientAsync()).ContinueWith(t =>
{
_tcpClient = t.Result;
_remoteAddress = ((IPEndPoint)_tcpClient.Client.RemoteEndPoint).Address.ToString();
_remotePort = ((IPEndPoint)_tcpClient.Client.RemoteEndPoint).Port;
_logger.Debug($"{MethodBase.GetCurrentMethod().Name} Connection Established from Remote Address: {_remoteAddress}:{_remotePort}");
// set timeouts
_tcpClient.Client.SendTimeout = (int)_defaultSendTimeout;
_tcpClient.Client.ReceiveTimeout = (int)_defaultReadTimeout;
// get the stream
_tcpIpStream = _tcpClient.GetStream();
_state = State.Ready;
});
}
else
{
_remotePort = _port;
_tcpClient = new TcpClient(_remoteAddress, _remotePort);
_logger.Debug($"{MethodBase.GetCurrentMethod().Name} Connected to Remote Address {_remoteAddress}:{_remotePort}");
// set timeouts
_tcpClient.Client.SendTimeout = (int)_defaultSendTimeout;
_tcpClient.Client.ReceiveTimeout = (int)_defaultReadTimeout;
// get the stream
_tcpIpStream = _tcpClient.GetStream();
_state = State.Ready;
}
}
/// <summary>
/// shuts down the device
/// </summary>
public void Shutdown()
{
_logger.Debug("Shutting down");
_tcpClient?.Dispose();
_tcpClient = null;
_tcpListener?.Stop();
_tcpListener = null;
_tcpIpStream?.Dispose();
_tcpIpStream = null;
_state = State.Uninitialized;
}
/// <summary>
/// Read data from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<uint> ReadAsync(byte[] dataRead, CancellationToken token = default)
{
if (_tcpIpStream == null)
return 0;
if (_tcpIpStream.DataAvailable == true)
{
_state = State.Busy;
var bytesRead = await _tcpIpStream.ReadAsync(dataRead, 0, dataRead.Length, token);
_logger.Trace($"Reading Data, bytes received: {bytesRead}");
_state = State.Ready;
return (uint)bytesRead;
}
else
{
return 0;
}
}
/// <summary>
/// Read string from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<string> ReadAsync(CancellationToken token = default)
{
if (_tcpIpStream == null)
return null;
if (_tcpIpStream.DataAvailable == true)
{
_state = State.Busy;
var buffer = new byte[_defaultReadBufferSize];
var bytesRead = await _tcpIpStream.ReadAsync(buffer, 0, buffer.Length, token);
_state = State.Ready;
var message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
_logger.Trace($"Reading Data, message received: {message}");
return message;
}
else
{
return null;
}
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
_logger.Trace($"Setting Reader Timeout: {timeoutMs} Ms");
_tcpClient.Client.ReceiveTimeout = (int)timeoutMs;
}
/// <summary>
/// Write data to the device asynchronously
/// </summary>
/// <param name="dataToSend"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
public async Task<uint> WriteAsync(byte[] dataToSend, uint numBytesToWrite, CancellationToken token = default)
{
if (_tcpIpStream == null)
return 0;
_logger.Trace($"Writing message to ({_remoteAddress}:{_remotePort}), bytes: {dataToSend?.Length}");
_state = State.Busy;
await _tcpIpStream.WriteAsync(dataToSend, 0, (int)numBytesToWrite, token);
_state = State.Ready;
return numBytesToWrite;
}
/// <summary>
/// Write string data to the device asynchronously
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task WriteAsync(string message, CancellationToken token = default)
{
if (_tcpIpStream == null)
return;
_logger.Trace($"Writing message to ({_remoteAddress}:{_remotePort}), message: {message}");
_state = State.Busy;
var buffer = Encoding.UTF8.GetBytes(message);
await _tcpIpStream.WriteAsync(buffer, 0, buffer.Length, token);
_state = State.Ready;
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<string> SendCommandGetResponseAsync(string message, CancellationToken cancellationToken = default, int timeoutInMs = 5000)
{
if (_tcpIpStream == null)
return null;
_logger.Trace($"Sending command waiting for response from ({_remoteAddress}:{_remotePort}), message: {message}");
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs)))
{
if (cancellationToken == default)
{
cancellationToken = cts.Token;
}
await WriteAsync(message, cancellationToken);
string readResponse = await ReadAsync(cancellationToken);
_logger.Trace($"Received response: {readResponse}");
return readResponse;
}
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<byte[]> SendCommandGetResponseAsync(byte[] data, CancellationToken cancellationToken = default, int timeoutInMs = 5000)
{
if (_tcpIpStream == null)
return null;
_logger.Trace($"Sending command waiting for response from ({_remoteAddress}:{_remotePort}), message: {data}");
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs)))
{
if (cancellationToken == default)
{
cancellationToken = cts.Token;
}
await WriteAsync(data, (uint)data.Length, cancellationToken);
byte[] buffer = new byte[_defaultReadBufferSize];
uint bytesRead = await ReadAsync(buffer, cancellationToken);
_logger.Trace($"Received response of size: {bytesRead}");
return buffer;
}
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<string> dataReceived)
{
_logger.Debug($"Starting continuous reading from {_remoteAddress}:{_remotePort} ...");
byte[] buffer = new byte[_defaultReadBufferSize];
while (!cancellationToken.IsCancellationRequested)
{
if (_tcpIpStream == null)
{
continue;
}
uint bytesRead = await ReadAsync(buffer, cancellationToken);
if (bytesRead > 0)
{
string data = Encoding.UTF8.GetString(buffer, 0, (int)bytesRead);
_logger.Trace($"Message received from {_remoteAddress}:{_remotePort}: {data}");
dataReceived(data);
Array.Clear(buffer, 0, (int)bytesRead);
}
}
_logger.Debug($"Finished continuous reading from {_remoteAddress}:{_remotePort} ...");
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<byte[]> dataReceived)
{
_logger.Debug($"Starting continuous reading from {_remoteAddress}:{_remotePort} ...");
byte[] buffer = new byte[_defaultReadBufferSize];
while (!cancellationToken.IsCancellationRequested)
{
if (_tcpIpStream == null)
{
continue;
}
uint bytesRead = await ReadAsync(buffer, cancellationToken);
if (bytesRead > 0)
{
_logger.Trace($"Message received from {_remoteAddress}:{_remotePort}: size {bytesRead}");
byte[] bufferCopy = new byte[bytesRead];
Array.Copy(buffer, bufferCopy, bytesRead);
dataReceived(bufferCopy);
Array.Clear(buffer, 0, (int)bytesRead);
}
}
_logger.Debug($"Finished continuous reading from {_remoteAddress}:{_remotePort} ...");
}
#endregion
}
}

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceTcpAsync</AssemblyName>
<Product>CommDevice TCP Asynchronous implementation</Product>
<Description>CommDevice TCP Asynchronous implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommAsync.Contracts" Version="1.4.0" />
</ItemGroup>
<!-- Copy all *.dlls and *.pdb in the output folder to a temp folder -->
<Target Name="CopyFiles" AfterTargets="AfterBuild">
<ItemGroup>
<FILES_1 Include="$(OutDir)*.dll" />
<FILES_2 Include="$(OutDir)*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(FILES_1)" DestinationFolder="$(HalTempFolder)" />
<Copy SourceFiles="@(FILES_2)" DestinationFolder="$(HalTempFolder)" />
</Target>
</Project>

View File

@@ -0,0 +1,136 @@
// **********************************************************************************************************
// CommDeviceTcpAsyncFactory.cs
// 3/6/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceTcpAsyncFactory")]
public class CommDeviceTcpAsyncFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceTcpAsyncFactory(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 CommDeviceTcpAsyncFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommAsync));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceTcpAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceTcpAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,389 @@
// 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.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Raytheon.Instruments
{
/// <summary>
/// Class for controlling a TCP client communication device
/// </summary>
public class CommDeviceTcpClient : ICommDevice, IDisposable
{
#region PrivateClassMembers
private const uint _DEFAULT_READ_TIMEOUT = 25;
private const uint _DEFAULT_SEND_TIMEOUT = 5000;
private const uint _DEFAULT_READ_BUFFER_SIZE = 1024;
private static object _syncObj = new object();
private TcpClient _tcpClient;
private NetworkStream _tcpIpStream;
private readonly int _remotePort;
private readonly string _remoteAddress;
private readonly string _name;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
public bool ClearErrors() => false;
public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); }
public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); }
public string DetailedStatus => $"This is a TCP/IP Device called {_name}";
public InstrumentMetadata Info => throw new NotImplementedException();
public State Status => _state;
public string Name => _name;
public SelfTestResult PerformSelfTest() => SelfTestResult;
public SelfTestResult SelfTestResult => SelfTestResult.Unknown;
public void Open() => Initialize();
public void Close() => Shutdown();
public void Reset()
{
Close();
Open();
}
#region Private Functions
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
try
{
lock (_syncObj)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close the socket
try
{
Shutdown();
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
}
#endregion
#region Public Functions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceTcpClient(string name, IConfigurationManager configurationManager, ILogger logger, string remoteAddress = "", int remotePort = 0)
{
_name = name;
// _tcpClient is created in Initialize()
_tcpClient = null;
_tcpIpStream = null;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
if(string.IsNullOrEmpty(remoteAddress))
{
_remoteAddress = _configuration.GetConfigurationValue("TcpClient", "RemoteAddress", "127.0.0.1");
}
else
{
_remoteAddress = remoteAddress;
}
if(remotePort == 0)
{
_remotePort = _configuration.GetConfigurationValue("TcpClient", "RemotePort", 0);
}
else
{
_remotePort = remotePort;
}
}
/// <summary>
/// legacy constructor
/// </summary>
/// <param name="name">The name of this device</param>
/// <param name="remoteAddress">The address of the server</param>
/// <param name="remotePort">The port that the server is listening on</param>
public CommDeviceTcpClient(string name, string remoteAddress, int remotePort)
{
_name = name;
_remotePort = remotePort;
_remoteAddress = remoteAddress;
// _tcpClient is created in Initialize()
_tcpClient = null;
_tcpIpStream = null;
_logger = LogManager.GetCurrentClassLogger();
_state = State.Uninitialized;
}
/// <summary>
/// initialize instrument
/// </summary>
public void Initialize()
{
lock (_syncObj)
{
if (_state == State.Uninitialized)
{
_tcpClient = new TcpClient(_remoteAddress, _remotePort);
// set timeouts
_tcpClient.Client.SendTimeout = (int)_DEFAULT_SEND_TIMEOUT;
_tcpClient.Client.ReceiveTimeout = (int)_DEFAULT_READ_TIMEOUT;
// get the stream
_tcpIpStream = _tcpClient.GetStream();
_state = State.Ready;
}
else
{
throw new Exception($"expected the state to be Uninitialized, state was: {_state} on device {_name}");
}
}
}
/// <summary>
/// shuts down the device
/// </summary>
public void Shutdown()
{
lock (_syncObj)
{
if (_state == State.Ready)
{
_tcpIpStream.Dispose();
_tcpClient.Dispose();
_state = State.Uninitialized;
}
}
}
/// <summary>
/// Read data from the device.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public uint Read(ref byte[] dataRead)
{
lock (_syncObj)
{
if (_tcpIpStream.DataAvailable == true)
{
_state = State.Busy;
uint numBytesRead = (uint)(_tcpIpStream.Read(dataRead, 0, dataRead.Length));
_state = State.Ready;
return numBytesRead;
}
else
{
return 0;
}
}
}
/// <summary>
/// Read data from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<uint> ReadAsync(byte[] dataRead)
{
if (_tcpIpStream.DataAvailable == true)
{
_state = State.Busy;
var bytesRead = await _tcpIpStream.ReadAsync(dataRead, 0, dataRead.Length);
_state = State.Ready;
return (uint)bytesRead;
}
else
{
return 0;
}
}
/// <summary>
/// Read string from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<string> ReadAsync(CancellationToken token = default)
{
if (_tcpIpStream.DataAvailable == true)
{
_state = State.Busy;
var buffer = new byte[_DEFAULT_READ_BUFFER_SIZE];
var bytesRead = await _tcpIpStream.ReadAsync(buffer, 0, buffer.Length, token);
_state = State.Ready;
return Encoding.UTF8.GetString(buffer, 0, bytesRead);
}
else
{
return null;
}
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
_tcpClient.Client.ReceiveTimeout = (int)timeoutMs;
}
/// <summary>
/// Write data to the device
/// </summary>
/// <param name="dataToSend">The data to write</param>
/// <param name="numBytesToWrite">The number of bytes to write</param>
/// <returns>The number of bytes that were written</returns>
public uint Write(byte[] dataToSend, uint numBytesToWrite)
{
lock (_syncObj)
{
_state = State.Busy;
_tcpIpStream.Write(dataToSend, 0, (int)numBytesToWrite);
_state = State.Ready;
return numBytesToWrite;
}
}
/// <summary>
/// Write data to the device asynchronously
/// </summary>
/// <param name="dataToSend"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
public async Task<uint> WriteAsync(byte[] dataToSend, uint numBytesToWrite)
{
_state = State.Busy;
await _tcpIpStream.WriteAsync(dataToSend, 0, (int)numBytesToWrite);
_state = State.Ready;
return numBytesToWrite;
}
/// <summary>
/// Write string data to the device asynchronously
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task WriteAsync(string message)
{
_state = State.Busy;
var buffer = Encoding.UTF8.GetBytes(message);
await _tcpIpStream.WriteAsync(buffer, 0, buffer.Length);
_state = State.Ready;
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<string> SendCommandGetResponseAsync(string message, int timeoutInMs = 5000)
{
_logger.Info($"Sending command waiting for response ({_remoteAddress}:{_remotePort}), message: {message}");
await WriteAsync(message);
CancellationTokenSource tokenSource = new CancellationTokenSource(new TimeSpan(0, 0, 0, 0, milliseconds:timeoutInMs));
string readResponse = await ReadAsync(tokenSource.Token);
_logger.Info($"Received response: {readResponse}");
return readResponse;
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<string> dataReceived)
{
_logger.Info($"About to start continuous reading from {_remoteAddress}:{_remotePort} ...");
byte[] buffer = new byte[_DEFAULT_READ_BUFFER_SIZE];
while (!cancellationToken.IsCancellationRequested)
{
int bytesRead = await _tcpIpStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
if (bytesRead > 0)
{
string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
_logger.Info($"Message received from {_remoteAddress}:{_remotePort}: {data}");
dataReceived(data);
Array.Clear(buffer, 0, bytesRead);
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceTcpClient</AssemblyName>
<Product>CommDevice TCP implementation</Product>
<Description>CommDevice TCP implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommDevice.Contracts" Version="1.2.0" />
</ItemGroup>
<!-- Copy all *.dlls and *.pdb in the output folder to a temp folder -->
<Target Name="CopyFiles" AfterTargets="AfterBuild">
<ItemGroup>
<FILES_1 Include="$(OutDir)*.dll" />
<FILES_2 Include="$(OutDir)*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(FILES_1)" DestinationFolder="$(HalTempFolder)" />
<Copy SourceFiles="@(FILES_2)" DestinationFolder="$(HalTempFolder)" />
</Target>
</Project>

View File

@@ -0,0 +1,104 @@
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceTcpClientFactory")]
public class CommDeviceTcpClientFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceTcpClientFactory(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 CommDeviceTcpClientFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommDevice));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceTcpClient(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceTcpClient(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,435 @@
// 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.Net;
using System.Net.Sockets;
namespace Raytheon.Instruments
{
/// <summary>
/// Class for controlling a UDP communication device
/// </summary>
public class CommDeviceUdp : ICommDevice, IDisposable
{
#region PrivateClassMembers
private const uint _DEFAULT_SEND_TIMEOUT = 5000;
private static readonly object _syncObj = new Object();
private UdpClient _udpClient;
private readonly int _localPort;
private readonly int _remotePort;
private readonly string _remoteAddress;
private IPEndPoint _remoteIPEndPoint;
private string _name;
private readonly SelfTestResult _selfTestResult;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
#region PrivateFunctions
/// <summary>
/// The Finalizer
/// </summary>
~CommDeviceUdp()
{
Dispose(false);
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close the socket and threads
try
{
if (_state == State.Ready)
{
_udpClient.Close();
_udpClient.Dispose();
_state = State.Uninitialized;
}
}
catch (Exception)
{
try
{
}
catch (Exception)
{
//Do not rethrow. Exception from error logger that has already been garbage collected
}
}
}
}
#endregion
#region PublicFuctions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceUdp(string deviceName, IConfigurationManager configurationManager, ILogger logger)
{
Name = deviceName;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
_localPort = _configuration.GetConfigurationValue("CommDeviceUdp", "LocalPort", 0);
_remotePort = _configuration.GetConfigurationValue("CommDeviceUdp", "RemotePort", 0);
_remoteAddress = _configuration.GetConfigurationValue("CommDeviceUdp", "RemoteAddress", "127.0.0.1");
// created in Initialize()
_udpClient = null;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
}
/// <summary>
///
/// </summary>
/// <param name="name">The name of this instance</param>
/// <param name="localPort">the port on the local computer to use</param>
/// <param name="remotePort">the port on the remote computer to send to</param>
/// <param name="remoteAddress">the address to send to</param>
public CommDeviceUdp(string name, int localPort, int remotePort, string remoteAddress)
{
_name = name;
_localPort = localPort;
_remotePort = remotePort;
_remoteAddress = remoteAddress;
// created in Initialize()
_udpClient = null;
_selfTestResult = SelfTestResult.Unknown;
_state = State.Uninitialized;
_logger = LogManager.GetCurrentClassLogger();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool ClearErrors()
{
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
public bool DisplayEnabled
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public string DetailedStatus
{
get
{
return "This is a UDP Device called " + _name;
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
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>
public InstrumentMetadata Info
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
public void Initialize()
{
lock (_syncObj)
{
if (_state == State.Uninitialized)
{
_udpClient = new UdpClient(_localPort);
_udpClient.Client.ReceiveBufferSize = int.MaxValue;
_udpClient.Client.SendBufferSize = int.MaxValue;
_udpClient.Client.SendTimeout = (int)_DEFAULT_SEND_TIMEOUT;
// set an arbitrary short receive timeout. Don't want the read call to block
_udpClient.Client.ReceiveTimeout = 5;
IPAddress remoteAddy = IPAddress.Parse(_remoteAddress);
_remoteIPEndPoint = new IPEndPoint(remoteAddy, _remotePort);
_state = State.Ready;
}
else
{
throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString() + " on device " + _name);
}
}
}
/// <summary>
///
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public SelfTestResult PerformSelfTest()
{
lock (_syncObj)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Read data from the device.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public uint Read(ref byte[] dataRead)
{
lock (_syncObj)
{
try
{
dataRead = _udpClient.Receive(ref _remoteIPEndPoint);
uint numBytesRead = (uint)(dataRead.Length);
return numBytesRead;
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.TimedOut)
{
// expected, do nothing
return 0;
}
else
{
throw;
}
}
}
}
/// <summary>
/// </summary>
public void Reset()
{
lock (_syncObj)
{
}
}
/// <summary>
///
/// </summary>
public SelfTestResult SelfTestResult
{
get
{
return _selfTestResult;
}
}
/// <summary>
///
/// </summary>
public State Status
{
get
{
return _state;
}
}
/// <summary>
///
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
lock (_syncObj)
{
_udpClient.Client.ReceiveTimeout = (int)timeoutMs;
}
}
/// <summary>
///
/// </summary>
public void Shutdown()
{
lock (_syncObj)
{
if (_state == State.Ready)
{
_udpClient.Close();
_udpClient.Dispose();
_state = State.Uninitialized;
}
}
}
/// <summary>
/// Write data to the device
/// </summary>
/// <param name="dataToSend">The data to write</param>
/// <param name="numBytesToWrite">The number of bytes to write</param>
/// <returns>THe number of bytes that were written</returns>
public uint Write(byte[] dataToSend, uint numBytesToWrite)
{
lock (_syncObj)
{
const uint MAX_BYTES_PER_PACKET = 65400;
uint index = 0;
if (numBytesToWrite > MAX_BYTES_PER_PACKET)
{
uint numPacketsToSend = numBytesToWrite / MAX_BYTES_PER_PACKET;
int packetsSent = 0;
while (packetsSent < numPacketsToSend)
{
Byte[] segment1 = new Byte[MAX_BYTES_PER_PACKET];
Array.Copy(dataToSend, index, segment1, 0, MAX_BYTES_PER_PACKET);
uint count = (uint)(_udpClient.Send(segment1, (int)MAX_BYTES_PER_PACKET, _remoteIPEndPoint));
index += count;
packetsSent++;
}
Byte[] segment = new Byte[MAX_BYTES_PER_PACKET];
uint numBytesRemaining = numBytesToWrite - index;
Array.Copy(dataToSend, index, segment, 0, numBytesRemaining);
index += (uint)(_udpClient.Send(segment, (int)numBytesRemaining, _remoteIPEndPoint));
}
else
{
index = (uint)(_udpClient.Send(dataToSend, (int)numBytesToWrite, _remoteIPEndPoint));
}
return index;
}
}
public void Close()
{
//throw new NotImplementedException();
}
public void Open()
{
//throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceUdp</AssemblyName>
<RootNamespace>Raytheon.Instruments</RootNamespace>
<Product>CommDevice UDP implementation</Product>
<Description>CommDevice UDP implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommDevice.Contracts" Version="1.2.0" />
</ItemGroup>
<!-- Copy all *.dlls and *.pdb in the output folder to a temp folder -->
<Target Name="CopyFiles" AfterTargets="AfterBuild">
<ItemGroup>
<FILES_1 Include="$(OutDir)*.dll" />
<FILES_2 Include="$(OutDir)*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(FILES_1)" DestinationFolder="$(HalTempFolder)" />
<Copy SourceFiles="@(FILES_2)" DestinationFolder="$(HalTempFolder)" />
</Target>
</Project>

View File

@@ -0,0 +1,104 @@
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceUdpFactory")]
public class CommDeviceUdpFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceUdpFactory(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 CommDeviceUdpFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommDevice));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceUdp(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceUdp(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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,468 @@
// **********************************************************************************************************
// CommDeviceUdpAsync.cs
// 3/6/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
namespace Raytheon.Instruments
{
/// <summary>
/// A sim communication device
/// </summary>
public class CommDeviceUdpAsync : ICommAsync
{
#region PrivateClassMembers
private uint _defaultReadTimeout;
private uint _defaultSendTimeout;
private uint _defaultReadBufferSize;
private static readonly object _syncObj = new object();
private UdpClient _udpClient;
private IPEndPoint _remoteEndPoint;
private int _localPort;
private int _remotePort;
private string _remoteAddress;
private readonly string _name;
private State _state;
/// <summary>
/// NLog logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
#endregion
public bool ClearErrors() => false;
public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); }
public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); }
public string DetailedStatus => $"This is a TCP/IP Device called {_name}";
public InstrumentMetadata Info => throw new NotImplementedException();
public State Status => _state;
public string Name => _name;
public SelfTestResult PerformSelfTest() => SelfTestResult;
public SelfTestResult SelfTestResult => SelfTestResult.Unknown;
public void Open() => Initialize();
public void Close() => Shutdown();
public void Reset()
{
Close();
Open();
}
#region Private Functions
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
public void Dispose()
{
try
{
lock (_syncObj)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
/// <summary>
/// Dispose of the resources contained by this object
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// close the socket
try
{
Shutdown();
}
catch (Exception err)
{
_logger.Error(err.Message + "\r\n" + err.StackTrace);
}
}
}
#endregion
#region Public Functions
/// <summary>
/// CommDevice factory constructor
/// </summary>
/// <param name="name"></param>
/// <param name="configurationManager"></param>
public CommDeviceUdpAsync(string name, IConfigurationManager configurationManager, ILogger logger)
{
_name = name;
_state = State.Uninitialized;
_logger = logger;
_configurationManager = configurationManager;
_configuration = _configurationManager.GetConfiguration(Name);
}
/// <summary>
/// initialize instrument
/// </summary>
public void Initialize()
{
if (_state != State.Uninitialized)
{
_logger.Warn("Reinitialization of existing UDP Async Connection. Attempting to call Shutdown.");
Shutdown();
}
_defaultReadTimeout = _configuration.GetConfigurationValue<uint>("UdpClient", "ReadTimeout", 25);
_defaultSendTimeout = _configuration.GetConfigurationValue<uint>("UdpClient", "SendTimeout", 5000);
_defaultReadBufferSize = _configuration.GetConfigurationValue<uint>("UdpClient", "BufferSize", 1024);
_localPort = _configuration.GetConfigurationValue("UdpClient", "LocalPort", 0);
_remoteAddress = _configuration.GetConfigurationValue("UdpClient", "RemoteAddress", "127.0.0.1");
_remotePort = _configuration.GetConfigurationValue("UdpClient", "RemotePort", 0);
_udpClient = new UdpClient();
if (string.IsNullOrEmpty(_remoteAddress))
{
_logger.Debug($"Initializing as UDP Server. Listening on port: {_localPort}");
_udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _localPort));
}
else
{
_logger.Debug($"Initializing as UDP Client. Ready to Talk to: {_remoteAddress}:{_remotePort}");
// get the remote endpoint
_remoteEndPoint = new IPEndPoint(IPAddress.Parse(_remoteAddress), _remotePort);
}
// set timeouts
_udpClient.Client.SendTimeout = (int)_defaultSendTimeout;
_udpClient.Client.ReceiveTimeout = (int)_defaultReadTimeout;
_state = State.Ready;
}
/// <summary>
/// shuts down the device
/// </summary>
public void Shutdown()
{
_logger.Debug("Shutting Down...");
_state = State.Uninitialized;
_udpClient?.Dispose();
_udpClient = null;
}
/// <summary>
/// Read data from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<uint> ReadAsync(byte[] dataRead, CancellationToken token = default)
{
if (_udpClient == null)
return 0;
var received = await _udpClient.ReceiveAsync();
Array.Copy(received.Buffer, dataRead, Math.Min(dataRead.Length, received.Buffer.Length));
UpdateRemoteAddressAndPort(received.RemoteEndPoint);
_logger.Trace($"Reading Data, bytes received: {received.Buffer?.Length}");
return (uint)received.Buffer.Length;
}
/// <summary>
/// Read string from the device asynchronously.
/// </summary>
/// <param name="dataRead">The buffer to put the data in</param>
/// <returns>The number of bytes read</returns>
public async Task<string> ReadAsync(CancellationToken token = default)
{
if (_udpClient == null)
return null;
var received = await _udpClient.ReceiveAsync();
UpdateRemoteAddressAndPort(received.RemoteEndPoint);
var data = Encoding.UTF8.GetString(received.Buffer);
_logger.Trace($"Reading Data, message received: {data}");
return data;
}
/// <summary>
/// Sets the read timeout
/// </summary>
/// <param name="timeoutMs"></param>
public void SetReadTimeout(uint timeoutMs)
{
if (_udpClient == null)
return;
_logger.Trace($"Setting Reader Timeout: {timeoutMs} Ms");
_udpClient.Client.ReceiveTimeout = (int)timeoutMs;
}
/// <summary>
/// Write data to the device asynchronously
/// </summary>
/// <param name="dataToSend"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
public async Task<uint> WriteAsync(byte[] dataToSend, uint numBytesToWrite, CancellationToken token = default)
{
if (_udpClient == null || _remoteEndPoint == null)
return 0;
_logger.Trace($"Writing message to ({_remoteAddress}:{_remotePort}), bytes: {dataToSend?.Length}");
_state = State.Busy;
await _udpClient.SendAsync(dataToSend, (int)numBytesToWrite, _remoteEndPoint);
_state = State.Ready;
return numBytesToWrite;
}
/// <summary>
/// Write string data to the device asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task WriteAsync(string message, CancellationToken token = default)
{
if (_udpClient == null || _remoteEndPoint == null)
return;
_logger.Trace($"Writing message to ({_remoteAddress}:{_remotePort}), message: {message}");
_state = State.Busy;
var dataToSend = Encoding.UTF8.GetBytes(message);
await _udpClient.SendAsync(dataToSend, dataToSend.Length, _remoteEndPoint);
_state = State.Ready;
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="message"></param>
/// <param name="cancellationToken"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<string> SendCommandGetResponseAsync(string message, CancellationToken cancellationToken = default, int timeoutInMs = 5000)
{
if (_udpClient == null)
return null;
_logger.Trace($"Sending command waiting for response from ({_remoteAddress}:{_remotePort}), message: {message}");
using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs)))
{
if (cancellationToken == default)
{
cancellationToken = cts.Token;
}
await WriteAsync(message, cancellationToken);
string readResponse = await ReadAsync(cancellationToken);
_logger.Trace($"Received response: {readResponse}");
return readResponse;
}
}
/// <summary>
/// Send Command and Get Response asynchronously
/// </summary>
/// <param name="data"></param>
/// <param name="cancellationToken"></param>
/// <param name="timeoutInMs"></param>
/// <returns></returns>
public async Task<byte[]> SendCommandGetResponseAsync(byte[] data, CancellationToken cancellationToken = default, int timeoutInMs = 5000)
{
if (_udpClient == null)
return null;
_logger.Trace($"Sending command waiting for response from ({_remoteAddress}:{_remotePort}), message length: {data.Length}");
using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs)))
{
if (cancellationToken == default)
{
cancellationToken = cts.Token;
}
await WriteAsync(data, (uint)data.Length, cancellationToken);
byte[] buffer = new byte[_defaultReadBufferSize];
uint bytesRead = await ReadAsync(buffer, cancellationToken);
_logger.Trace($"Received response of size: {bytesRead}");
return buffer;
}
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<string> dataReceived)
{
if (_udpClient == null)
return;
_logger.Debug($"Starting continuous reading from port: {_localPort} ...");
while (!cancellationToken.IsCancellationRequested)
{
if (_udpClient == null)
break;
var received = await ReceiveAsync(_udpClient, cancellationToken);
if (received != null && received.Buffer != null)
{
UpdateRemoteAddressAndPort(received.RemoteEndPoint);
_logger.Trace($"Incoming Data from {_remoteAddress}:{_remotePort}: size {received.Buffer.Length}");
string data = Encoding.UTF8.GetString(received.Buffer, 0, received.Buffer.Length);
dataReceived(data);
}
}
_logger.Debug($"Finished continuous reading from {_remoteAddress}:{_remotePort} ...");
}
/// <summary>
/// keeps reading until canceled via token,
/// received messages sent to dataReceived function
/// </summary>
/// <param name="cancellationToken"></param>
/// <param name="dataReceived"></param>
/// <returns></returns>
public async Task KeepReadingAsync(CancellationToken cancellationToken, Action<byte[]> dataReceived)
{
if (_udpClient == null)
return;
_logger.Debug($"Starting continuous reading from port: {_localPort} ...");
while (!cancellationToken.IsCancellationRequested)
{
if (_udpClient == null)
break;
var received = await ReceiveAsync(_udpClient, cancellationToken);
if (received != null && received.Buffer != null)
{
UpdateRemoteAddressAndPort(received.RemoteEndPoint);
_logger.Trace($"Incoming Data from {_remoteAddress}:{_remotePort}: size {received.Buffer.Length}");
dataReceived(received.Buffer);
}
}
_logger.Debug($"Finished continuous reading from {_remoteAddress}:{_remotePort} ...");
}
#endregion
#region Private Functions
/// <summary>
/// Update client information for logging
/// </summary>
/// <param name="remoteEndPoint"></param>
private void UpdateRemoteAddressAndPort(IPEndPoint remoteEndPoint)
{
if (remoteEndPoint == null)
return;
if (_remotePort == 0 || string.IsNullOrEmpty(_remoteAddress))
{
_remotePort = remoteEndPoint.Port;
_remoteAddress = remoteEndPoint.Address.ToString();
}
if(_remoteEndPoint == null || _remoteEndPoint.Port != remoteEndPoint.Port )
{
// get the remote endpoint
_remoteEndPoint = remoteEndPoint;
_logger.Debug($"Starting to receive data from {_remoteAddress}:{_remotePort}");
}
}
/// <summary>
/// ReceiveAsyc with cancellation token implementation
/// </summary>
/// <param name="client"></param>
/// <param name="breakToken"></param>
/// <returns></returns>
private Task<UdpReceiveResult> ReceiveAsync(UdpClient client, CancellationToken breakToken) => breakToken.IsCancellationRequested
? Task.Run(() => new UdpReceiveResult())
: Task<UdpReceiveResult>.Factory.FromAsync
((callback, state) => client.BeginReceive(callback, state), (ar) =>
{
if (breakToken.IsCancellationRequested)
return new UdpReceiveResult();
IPEndPoint remoteEP = null;
var buffer = client.EndReceive(ar, ref remoteEP);
return new UdpReceiveResult(buffer, remoteEP);
},null);
#endregion
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Solution.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Raytheon.Instruments.CommDeviceUdpAsync</AssemblyName>
<RootNamespace>Raytheon.Instruments</RootNamespace>
<Product>CommDevice UDP Asynchronous implementation</Product>
<Description>CommDevice UDP Asynchronous implementation</Description>
<OutputType>Library</OutputType>
<!-- Static versioning (Suitable for Development) -->
<!-- Disable the line below for dynamic versioning -->
<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.CommAsync.Contracts" Version="1.4.0" />
</ItemGroup>
<!-- Copy all *.dlls and *.pdb in the output folder to a temp folder -->
<Target Name="CopyFiles" AfterTargets="AfterBuild">
<ItemGroup>
<FILES_1 Include="$(OutDir)*.dll" />
<FILES_2 Include="$(OutDir)*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(FILES_1)" DestinationFolder="$(HalTempFolder)" />
<Copy SourceFiles="@(FILES_2)" DestinationFolder="$(HalTempFolder)" />
</Target>
</Project>

View File

@@ -0,0 +1,136 @@
// **********************************************************************************************************
// CommDeviceUdpAsyncFactory.cs
// 3/6/2024
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// 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.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
namespace Raytheon.Instruments
{
[ExportInstrumentFactory(ModelNumber = "CommDeviceUdpAsyncFactory")]
public class CommDeviceUdpAsyncFactory : IInstrumentFactory
{
/// <summary>
/// The supported interfaces
/// </summary>
private readonly List<Type> _supportedInterfaces = new List<Type>();
private ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private const string DefaultConfigPath = @"C:\ProgramData\Raytheon\InstrumentManagerService";
private static string DefaultPath;
public CommDeviceUdpAsyncFactory(string defaultConfigPath = DefaultConfigPath)
: this(null, defaultConfigPath)
{
}
/// <summary>
/// CommDeviceUdpAsyncFactory injection constructor
/// </summary>
/// <param name="configManager"></param>
/// <param name="simEngine"></param>
/// <param name="logger"></param>
[ImportingConstructor]
public CommDeviceUdpAsyncFactory([Import(AllowDefault = false)] IConfigurationManager configManager,
[Import(AllowDefault = true)] string defaultConfigPath = null)
{
DefaultPath = defaultConfigPath;
if (LogManager.Configuration == null)
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config");
}
_configurationManager = configManager ?? GetConfigurationManager();
_supportedInterfaces.Add(typeof(ICommAsync));
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IInstrument GetInstrument(string name)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceUdpAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets the instrument
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object GetInstrument(string name, bool simulateHw)
{
try
{
_logger = LogManager.GetLogger(name);
return new CommDeviceUdpAsync(name, _configurationManager, _logger);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Gets supported interfaces
/// </summary>
/// <returns></returns>
public ICollection<Type> GetSupportedInterfaces()
{
return _supportedInterfaces.ToArray();
}
/// <summary>
/// returns configuration 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);
}
}
}