// ********************************************************************************************************** // 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 System; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; using NLog; using Raytheon.Common; namespace Raytheon.Instruments { /// /// A sim communication device /// public class CommDeviceSerialAsync : ICommAsync { #region PrivateClassMembers private uint _defaultReadTimeout; private uint _defaultSendTimeout; private uint _defaultReadBufferSize; private object _syncObj = new object(); private SerialPort _serialPort; private string _comPortName; private int _baudRate; private Parity _parity; private int _dataBits; private StopBits _stopBits; private readonly string _name; private State _state; private readonly ILogger _logger; 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 Public Functions /// /// CommDevice factory constructor /// /// /// public CommDeviceSerialAsync(string deviceName, IConfigurationManager configurationManager) { _name = deviceName; _configurationManager = configurationManager; _configuration = _configurationManager.GetConfiguration(deviceName); _logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}"); _comPortName = _configuration.GetConfigurationValue(_name, "COMPortName"); int.TryParse(_configuration.GetConfigurationValue(_name, "BaudRate"), out _baudRate); Enum.TryParse(_configuration.GetConfigurationValue(_name, "Parity"), true, out _parity); int.TryParse(_configuration.GetConfigurationValue(_name, "DataBits"), out _dataBits); Enum.TryParse(_configuration.GetConfigurationValue(_name, "StopBits"), true, out _stopBits); uint.TryParse(_configuration.GetConfigurationValue(_name, "ReadTimeout"), out _defaultReadTimeout); uint.TryParse(_configuration.GetConfigurationValue(_name, "SendTimeout"), out _defaultSendTimeout); uint.TryParse(_configuration.GetConfigurationValue(_name, "BufferSize"), out _defaultReadBufferSize); _state = State.Uninitialized; } /// /// Destructor /// ~CommDeviceSerialAsync() { Shutdown(); } /// /// initialize instrument /// public void Initialize() { Open(); } /// /// Opens COM serial port for communications /// public void Open() { if (_state == State.Uninitialized) { _serialPort = new SerialPort(_comPortName, _baudRate, _parity, _dataBits, _stopBits); _serialPort.Open(); _state = State.Ready; } } /// /// shuts down the device /// public void Shutdown() { if (_serialPort != null) _serialPort.Close(); _state = State.Uninitialized; } /// /// Read data from the device asynchronously. /// /// The buffer to put the data in /// The number of bytes read public async Task ReadAsync(byte[] dataRead, CancellationToken token = default) { var bytesRead = await _serialPort.BaseStream.ReadAsync(dataRead, 0, dataRead.Length, token); return (uint)bytesRead; } /// /// Read string from the device asynchronously. /// /// The buffer to put the data in /// The number of bytes read public async Task ReadAsync(CancellationToken token = default) { var data = await ReadLineAsync(token); return data; } /// /// Sets the read timeout /// /// public void SetReadTimeout(uint timeoutMs) { if (_serialPort == null) return; _serialPort.ReadTimeout = (int)timeoutMs; } /// /// Write data to the device asynchronously /// /// /// /// public async Task WriteAsync(byte[] dataToSend, uint numBytesToWrite, CancellationToken token = default) { if (_serialPort == null || !_serialPort.IsOpen) return 0; await _serialPort.BaseStream.WriteAsync(dataToSend, 0, (int)numBytesToWrite, token); return numBytesToWrite; } /// /// Write string data to the device asynchronously /// /// /// public async Task WriteAsync(string message, CancellationToken token = default) { if (_serialPort == null || !_serialPort.IsOpen) return; await WriteLineAsync(message, token); } /// /// Send Command and Get Response asynchronously /// /// /// /// public async Task SendCommandGetResponseAsync(string message, CancellationToken cancellationToken = default, int timeoutInMs = 5000) { if (_serialPort == null || !_serialPort.IsOpen) return null; using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutInMs))) { if (cancellationToken == default) { cancellationToken = cts.Token; } await WriteAsync(message, cancellationToken); string readResponse = await ReadAsync(cancellationToken); return readResponse; } } /// /// Send Command and Get Response asynchronously /// /// /// /// public async Task SendCommandGetResponseAsync(byte[] data, CancellationToken token = default, int timeoutInMs = 5000) { if (_serialPort == null || !_serialPort.IsOpen) return null; await WriteAsync(data, (uint)data.Length, token); _serialPort.ReadTimeout = timeoutInMs; var response = new byte[data.Length]; await ReadAsync(response, token); return response; } /// /// keeps reading until canceled via token, /// received messages sent to dataReceived function /// /// /// /// public async Task KeepReadingAsync(CancellationToken cancellationToken, Action dataReceived) { if (_serialPort == null || !_serialPort.IsOpen) return; while (!cancellationToken.IsCancellationRequested) { var data = await ReadAsync(cancellationToken); dataReceived?.Invoke(data); } } /// /// keeps reading until canceled via token, /// received messages sent to dataReceived function /// /// /// /// public async Task KeepReadingAsync(CancellationToken cancellationToken, Action dataReceived) { if (_serialPort == null || !_serialPort.IsOpen) return; 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); } } #endregion #region private functions /// /// reads line async /// /// /// private async Task 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; } } /// /// writes line async /// /// /// /// 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 } }