// ********************************************************************************************************** // COECommDeviceInstrument.cs // 7/11/2022 // 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 Raytheon.Instruments.MessagingUtilities; using System.Xml.XPath; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using System.Threading; using Raytheon.Common; using Raytheon.Instruments.coeCSharp; using System.Xml; using System.IO; using System.Reflection; using System.Globalization; using NLog; namespace Raytheon.Instruments { /// /// This device supports different ways of communicating with other COE nodes /// TCP /// UDP /// Serial /// public enum DriverType { Undefined, TCP, UDP, Serial }; public class COECommDeviceInstrument : ICommDevice { /// /// Nlog Logger /// readonly ILogger _logger; /// /// Cancellation token to stop reading incoming messages /// private CancellationTokenSource _cancellationTokenSource = null; /// /// collection of the messages received /// private readonly Dictionary, OeMessage> _messages; /// /// Raytheon configuration /// private readonly IConfigurationManager _configurationManager; private readonly IConfiguration _configuration; /// /// UDP, TCP, Serial or Undefined /// private DriverType _driverType; /// /// dictionary of options when initializing COE endpoint and router /// private readonly Dictionary>> _options = new Dictionary>>(); /// /// reference to the main wrapper class for Common Operating Environment /// private readonly coe _coe; /// /// COE endpoint /// private coeEndpoint _endpoint; /// /// used for initialization of the endpoint /// private uint _maxMessageSize; private uint _epQueueDepth; /// /// collection of all labels with message names per every XML file /// private readonly Dictionary> _icds = new Dictionary>(); /// /// timeout can be set for the reader in a separate call /// private uint _receiverTimeout; /// /// Number of milliseconds to wake up and check the message when receiving /// private int _checkForMessageIntervalMs; /// /// collection of response labels or messages that COE endpoint should be registered for /// private List _responseLabels = new List(); /// /// collection of message XML documents (processed XML files) used in COE communications /// private readonly Dictionary _xmlDocs = new Dictionary(); /// /// instrument constructor /// public COECommDeviceInstrument(string name, IConfigurationManager configurationManager, DriverType driverType = DriverType.Undefined, ILogger logger = null) { Info = new InstrumentMetadata { ModelNumber = "COECommDevice" }; if(logger == null) logger = LogManager.GetCurrentClassLogger(); _logger = logger; Status = State.Uninitialized; DetailedStatus = "COE Uninitialized"; Name = name; if (LogManager.Configuration == null) { var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(assemblyFolder + "\\nlog.config"); } if (configurationManager == null) { _logger.Error($"Cannot create {Name} without a configuration manager"); return; } _configurationManager = configurationManager; _configuration = _configurationManager.GetConfiguration(Name); _messages = new Dictionary, OeMessage>(); _driverType = driverType; _coe = new coe(); } public string DetailedStatus { get; protected set; } public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); } public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); } public InstrumentMetadata Info { get; set; } public string Name { get; protected set; } public SelfTestResult SelfTestResult => PerformSelfTest(); public State Status { get; set; } public bool ClearErrors() { return true; } /// /// Initializes COE instrument /// public void Initialize() { _logger.Trace($"{Name}({_driverType}) Initializing..."); if (_driverType == DriverType.Undefined) _driverType = _configuration.GetConfigurationValue("Parameters", "DriverType", "TCP"); _options.Clear(); _options.Add("ROUTER_CONFIG", new List> { { new KeyValuePair("NODE_ID", _configuration.GetConfigurationValue("ROUTER_CONFIG", "NODE_ID", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_STATE", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_STATE", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_LABEL_MESSAGE", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_LABEL_MESSAGE", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_BRIDGE_REGISTRATION", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_BRIDGE_REGISTRATION", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_ROUTER_DATABASE", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_ROUTER_DATABASE", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("ROUTER_CONFIG", "DISPLAY_DEBUG_RECV", "0")) }, { new KeyValuePair("BUFFER_SIZE", _configuration.GetConfigurationValue("ROUTER_CONFIG", "BUFFER_SIZE", "256")) }, { new KeyValuePair("ENABLE_REGISTRATION_MESSAGES", _configuration.GetConfigurationValue("ROUTER_CONFIG", "ENABLE_REGISTRATION_MESSAGES", "1")) }, { new KeyValuePair("THREAD_STACK_SIZE", _configuration.GetConfigurationValue("ROUTER_CONFIG", "THREAD_STACK_SIZE", "16384")) }, }); _options.Add("ROUTER_PROTOCOL_CONFIG", new List> { { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("ROUTER_PROTOCOL_CONFIG", "DISPLAY_DEBUG_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("ROUTER_PROTOCOL_CONFIG", "DISPLAY_DEBUG_RECV", "0")) }, { new KeyValuePair("THREAD_STACK_SIZE", _configuration.GetConfigurationValue("ROUTER_PROTOCOL_CONFIG", "THREAD_STACK_SIZE", "16384")) }, }); var poolEntry = _configuration.GetConfigurationValue("ROUTER_BUFFER_POOLS", "POOL_ENTRY", "100,32|50,128|100,384|150,1536|10,65535"); if (!string.IsNullOrEmpty(poolEntry)) { var poolEntries = poolEntry.Split('|'); if (poolEntries.Any()) { var entries = new List>(); foreach (var entry in poolEntries) { entries.Add(new KeyValuePair("POOL_ENTRY", entry)); } _options.Add("ROUTER_BUFFER_POOLS", entries); } } _options.Add("BASIC_REGISTRATION_CONFIG", new List> { { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND_BUFFER", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_SEND_BUFFER", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_RECV", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV_BUFFER", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_RECV_BUFFER", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_STATE", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_STATE", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_PING_SEND", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_PING_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_PING_RECV", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_PING_RECV", "0")) }, { new KeyValuePair("THREAD_STACK_SIZE", _configuration.GetConfigurationValue("BASIC_REGISTRATION_CONFIG", "THREAD_STACK_SIZE", "16384")) }, }); switch (_driverType) { case DriverType.UDP: _options.Add("UDP_MEDIA_BINDING_CONFIG", new List> { { new KeyValuePair("LOCAL_IP_ADDRESS", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "LOCAL_IP_ADDRESS", "127.0.0.1")) }, { new KeyValuePair("REMOTE_IP_ADDRESS", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "REMOTE_IP_ADDRESS", "127.0.0.1")) }, { new KeyValuePair("LOCAL_SEND_PORT", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "LOCAL_SEND_PORT", "32010")) }, { new KeyValuePair("LOCAL_RECV_PORT", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "LOCAL_RECV_PORT", "32020")) }, { new KeyValuePair("REMOTE_SEND_PORT", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "REMOTE_SEND_PORT", "32011")) }, { new KeyValuePair("REMOTE_RECV_PORT", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "REMOTE_RECV_PORT", "32021")) }, { new KeyValuePair("RECV_TIMEOUT", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "RECV_TIMEOUT", "200")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "0")) }, { new KeyValuePair("MTU_SIZE", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "MTU_SIZE", "1472")) }, { new KeyValuePair("THREAD_STACK_SIZE", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "THREAD_STACK_SIZE", "16384")) }, { new KeyValuePair("THREAD_NAME", _configuration.GetConfigurationValue("UDP_MEDIA_BINDING_CONFIG", "THREAD_NAME", "UDP_MB_RCV")) }, }); break; case DriverType.TCP: _options.Add("TCP_MEDIA_BINDING_CONFIG", new List> { { new KeyValuePair("LOCAL_PORT", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "LOCAL_PORT", "9990")) }, { new KeyValuePair("NUM_PORTS", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "NUM_PORTS", "32")) }, { new KeyValuePair("NUM_DYNAMIC_NODES", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "NUM_DYNAMIC_NODES", "32")) }, { new KeyValuePair("SERVER_ADDRESS", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "SERVER_ADDRESS", "127.0.0.1:9990")) }, { new KeyValuePair("UDP_TX_BUFFER_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "UDP_TX_BUFFER_SIZE", "5000")) }, { new KeyValuePair("UDP_RX_BUFFER_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "UDP_RX_BUFFER_SIZE", "32768")) }, { new KeyValuePair("TCP_TX_BUFFER_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "TCP_TX_BUFFER_SIZE", "5000")) }, { new KeyValuePair("TCP_RX_BUFFER_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "TCP_RX_BUFFER_SIZE", "4096")) }, { new KeyValuePair("PACKET_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "PACKET_SIZE", "5128")) }, { new KeyValuePair("TCP_SELECT_VALUE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "TCP_SELECT_VALUE", "1")) }, { new KeyValuePair("DISABLE_NAG_DELAY", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISABLE_NAG_DELAY", "1")) }, { new KeyValuePair("TIMER_RATE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "TIMER_RATE", "1000")) }, { new KeyValuePair("CONNECT_KA_RATE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "CONNECT_KA_RATE", "1")) }, { new KeyValuePair("RECV_KA_RATE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "RECV_KA_RATE", "1")) }, { new KeyValuePair("SERVER_CONNECT_RATE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "SERVER_CONNECT_RATE", "1")) }, { new KeyValuePair("RECV_THREAD_STACK_SIZE", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_STACK_SIZE", "4096")) }, { new KeyValuePair("RECV_THREAD_PRIORITY", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_PRIORITY", "0")) }, { new KeyValuePair("RECV_THREAD_AFFINITY", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_AFFINITY", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND_BUFFER", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND_BUFFER", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_UDP_RECV", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_UDP_RECV", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_UDP_RECV_BUFFER", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_UDP_RECV_BUFFER", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_TCP_RECV", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_TCP_RECV", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_TCP_RECV_BUFFER", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_TCP_RECV_BUFFER", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "1")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV_BUFFER", _configuration.GetConfigurationValue("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV_BUFFER", "1")) }, }); break; case DriverType.Serial: _options.Add("SERIAL_MEDIA_BINDING_CONFIG", new List> { { new KeyValuePair("DEVICE_NAME", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "DEVICE_NAME", "\\\\.\\COM1")) }, { new KeyValuePair("BAUD_RATE", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "BAUD_RATE", "9600")) }, { new KeyValuePair("DATA_BITS", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "DATA_BITS", "8")) }, { new KeyValuePair("STOP_BITS", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "STOP_BITS", "1")) }, { new KeyValuePair("PARITY", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "PARITY", "0")) }, { new KeyValuePair("FLOW_CONTROL", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "FLOW_CONTROL", "0")) }, { new KeyValuePair("MTU_SIZE", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "MTU_SIZE", "256")) }, { new KeyValuePair("RECV_PROCESSING_DELAY", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "RECV_PROCESSING_DELAY", "100")) }, { new KeyValuePair("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "0")) }, { new KeyValuePair("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue("SERIAL_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "0")) }, }); break; default: _logger.Error($"{Name}({_driverType}) Configured driver type not valid"); break; } _maxMessageSize = _configuration.GetConfigurationValue("Parameters", "MaxMessageSize", "5000"); _epQueueDepth = _configuration.GetConfigurationValue("Parameters", "EPQueueDepth", "5000"); _checkForMessageIntervalMs = _configuration.GetConfigurationValue("Parameters", "CheckForMessageIntervalMs", "100"); var responseLabels = _configuration.GetConfigurationListValue("ResponseMessageIds", "ResponseLabel", new List { "1", "2" }); var bitFilePaths = _configuration.GetConfigurationListValue("BitFilePaths", "FilePath", new List { "File1", "File2" }); _xmlDocs.Clear(); foreach (var path in bitFilePaths) { _xmlDocs.Add(path, new MessageXmlDocument(path, _logger)); } _icds.Clear(); foreach (var path in bitFilePaths) { _icds.Add(path, ProcessFileForNamesAndLabels(path)); } foreach (var strLabel in responseLabels) { uint label = GetLabelFromMessageId(strLabel); if(label > 0) _responseLabels.Add(label); } DetailedStatus = "COE Initialized"; Status = State.Ready; } /// /// performs self-test /// /// public SelfTestResult PerformSelfTest() { _logger.Trace($"{Name}({_driverType}) Performing Self Test..."); // TODO implement method return SelfTestResult.Pass; } /// /// Resets COE device comms /// /// public void Reset() { _logger.Trace($"{Name}({_driverType}) Resetting..."); Close(); Open(); } /// /// Shuts down COE device /// public void Shutdown() { _logger.Trace($"{Name}({_driverType}) Shutting Down..."); try { Close(); //coe.UnloadImportedDll("coeWindows-shared.dll"); } catch (Exception ex) { _logger.Error(ex, $"{Name}({_driverType}) Error while closing"); } } #region ICommDevice functions /// /// Opens COE connection /// /// public void Open() { _logger.Trace($"{Name}({_driverType}) Opening..."); try { switch (_driverType) { case DriverType.TCP: if (_coe.tcp_media_binding_configure(_options, _logger) != coe.Status.SUCCESS) { _logger.Error($"{Name}({_driverType}) COE TCP media binding initialization failure.\nTry checking your connection configuration.\nYou could have a port collision."); Status = State.CommunicationFailure; throw new Exception($"{Name}({_driverType}) COE TCP media binding initialization failure"); } _logger.Trace($"{Name}({_driverType}) COE TCP media binding initialization, {_coe.ProtocolCmitName}"); break; case DriverType.UDP: if (_coe.udp_media_binding_configure(_options, _logger) != coe.Status.SUCCESS) { _logger.Error($"{Name}({_driverType}) COE UDP media binding initialization failure.\nTry checking your connection configuration.\nYou could have a port collision."); Status = State.CommunicationFailure; throw new Exception($"{Name}({_driverType}) COE UDP media binding initialization failure"); } _logger.Trace($"{Name}({_driverType}) COE UDP media binding initialization, Local: {_coe.ProtocolCmitName} Remote: {_coe.ProtocolName}"); break; case DriverType.Serial: if (_coe.serial_media_binding_configure(_options, _logger) != coe.Status.SUCCESS) { _logger.Error($"{Name}({_driverType}) COE Serial media binding initialization failure.\nTry checking your connection configuration.\nYou could have a port collision."); Status = State.CommunicationFailure; throw new Exception($"{Name}({_driverType}) COE Serial media binding initialization failure"); } _logger.Trace($"{Name}({_driverType}) COE Serial media binding initialization, {_coe.ProtocolCmitName}"); break; default: _logger.Error($"{Name}({_driverType}) Configured driver type not valid"); throw new Exception($"{Name}({_driverType}) Configured driver type not valid"); } foreach (var item in _options) { _logger.Trace($"{item.Key}:"); foreach (var pair in item.Value) { _logger.Trace(string.Format("{0,-50} {1, -40}", pair.Key, pair.Value)); } } } catch (Exception ex) { _logger.Error(ex); Status = State.CommunicationFailure; DetailedStatus = "Unable to Open"; throw; } try { _coe.SetConnected(true); //_endpoint = new coeEndpoint(_maxMessageSize, _epQueueDepth, coe.Router); _endpoint = new coeEndpoint(_maxMessageSize, _epQueueDepth); _logger.Info($"{Name}({_driverType}) Endpoint Created, Max Message Size: {_maxMessageSize}, Queue Depth: {_epQueueDepth}"); foreach (var item in _responseLabels) { var fileName = WhichFileContainsTheLabel(item); if (!string.IsNullOrEmpty(fileName)) { var msgName = _icds[fileName][item]; if (!string.IsNullOrEmpty(msgName)) { _endpoint.Register(item); _logger.Debug($"{Name}({_driverType}) Registering new message with the endpoint, {item}: {msgName}"); } else { _logger.Warn($"{Name}({_driverType}) Message with label {item} is not located in file {fileName}"); } } else { _logger.Warn($"{Name}({_driverType}) Unable to locate label {item} in any of the XML files registered for COE device"); } } _cancellationTokenSource = new CancellationTokenSource(); Task.Run(() => ReadMessages(_cancellationTokenSource.Token)); DetailedStatus = "Opened"; } catch (Exception ex) { Status = State.CommunicationFailure; DetailedStatus = "Unable to Open"; _logger.Error(ex); throw; } } /// /// Close COE endpoint /// /// public void Close() { _logger.Trace($"{Name}({_driverType}) Closing ..."); _cancellationTokenSource?.Cancel(); Thread.Sleep(1000); lock (this) { _messages.Clear(); } _coe.SetConnected(false); var status = _driverType == DriverType.TCP ? _coe.TCP_media_binding_shutdown(_logger) : _driverType == DriverType.UDP ? _coe.UDP_media_binding_shutdown(_logger) : _coe.SERIAL_media_binding_shutdown(_logger); if (status != coe.Status.SUCCESS) { _logger.Error($"{_driverType} media binding shutdown failure, status {status}"); } else { _logger.Debug($"{_driverType} shutdown was successful"); } _endpoint?.Dispose(); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; Status = State.Uninitialized; DetailedStatus = "COE Closed"; } /// /// sets the timeout value for COE reading operation /// /// /// public void SetReadTimeout(uint timeout) { _logger.Trace($"{Name}({_driverType}) Setting read timeout {timeout} ms ..."); _receiverTimeout = timeout; } /// /// reads COE message either based on the label or just top message /// /// /// might contain a COE label /// /// public uint Read(ref byte[] dataRead) { _logger.Trace($"{Name}({_driverType}) Reading ..."); if (!_coe.IsConnected) { _logger.Error("Error reading COE message, COE not connected"); return 0; } // if the label was provided in the byte array use it to locate the first message with that name // else just fetch the top message var label = FromByteArrayToXml(dataRead); KeyValuePair, OeMessage> message; lock (this) { message = string.IsNullOrEmpty(label) ? _messages.FirstOrDefault() : _messages.FirstOrDefault(m => m.Key.Item1 == label); } if (message.Value != null) { // make a copy of the buffer var buffer = message.Value.GetManagedBuffer(); if(buffer != null) { Array.Copy(buffer, 0, dataRead, 0, buffer.Length); lock (this) { if (!_messages.Remove(message.Key)) _logger.Warn($"{Name}({_driverType}) Unable to remove a message from the dictionary, label = {message.Key}"); } } else { _logger.Error($"{Name}({_driverType}) Found a message with label {label}, but the Buffer is empty"); return 0; } } else { _logger.Error(string.IsNullOrEmpty(label) ? $"{Name}({_driverType}) No messages available to read at this time" : $"{Name}({_driverType}) Unable to find message with label {label}"); return 0; } return (uint)dataRead.Length; } /// /// keep reading messages and stash them in _messages dictionary with label and timestamp as a key /// /// /// private void ReadMessages(CancellationToken cancellationToken) { _logger.Debug($"{Name}({_driverType}) Starting to read messages."); try { while (!cancellationToken.IsCancellationRequested) { _logger.Debug($"{Name}({_driverType}) Checking for messages..."); var status = _endpoint.Wait(1000); if (status == coe.Status.SUCCESS) { _logger.Debug($"{Name}({_driverType}) Message Received..."); while (_endpoint.Peek(out uint label, out uint size, out int priority) == coe.Status.SUCCESS) { _logger.Debug($"{Name}({_driverType}) Identified message by peeking..."); var xmlDoc = WhichFileContainsTheLabel(label); var message = new OeMessage((int)size + 1) { XmlMessage = new Message(_xmlDocs[xmlDoc], label.ToString()) }; status = _endpoint.Receive(message); if (status == coe.Status.SUCCESS) { _logger.Debug($"{Name}({_driverType}) Successfully read message..."); lock (this) { _messages.Add(new Tuple(message.Label, DateTime.Now), message); } } else { _logger.Error($"{Name}({_driverType}) Endpoint Receive Failed. Status = {status}"); } } } // If not timeout and no cancellation requested else if (status != coe.Status.FAILED_TIMEOUT && !cancellationToken.IsCancellationRequested) { _logger.Error($"{Name}({_driverType}) Event Flag Wait Failed. Status = {status}"); } } _logger.Debug($"{Name}({_driverType}) Stopping to read messages. Cancellation was requested."); } catch (Exception ex) { _logger.Error(ex); } } /// /// Writes COE message /// /// /// /// public uint Write(byte[] data, uint numBytesToWrite) { _logger.Debug($"{Name}({_driverType}) Starting to write messages."); if (!_coe.IsConnected) { _logger.Error($"{Name}({_driverType}) Error sending COE message, COE not connected"); return (uint)coe.Status.FAILED_NOT_ENABLED; } try { var xmlData = FromByteArrayToXml(data); var messageName = GetMessageNameFromXml(xmlData); var message = new OeMessage(new Message(messageName, new MessageXmlDocument(xmlData, _logger))); //var status = _endpoint.Send(message, (uint)(_driverType == DriverType.TCP ? 0x01 : 0)); var status = _endpoint.Send(message); if (status != coe.Status.SUCCESS) { _logger.Error($"{Name}({_driverType}) Error sending COE message, error code: {status}"); } return status == coe.Status.ERROR ? (uint)coe.Status.FAILED_INTERNAL_ERROR : (uint)status; } catch (Exception ex) { _logger.Error(ex, $"{Name}({_driverType}) Error while writing a message"); return (uint)coe.Status.FAILED_INTERNAL_ERROR; } } #endregion /// /// if byte array contains byte representation of XML then returns XML string /// else returns empty string /// /// /// private static string FromByteArrayToXml(byte[] data) => System.Text.Encoding.Default.GetString(data); private static byte[] FromStringToByteArray(string data) => System.Text.Encoding.UTF8.GetBytes(data); /// /// Extract message node from XML for the OeMessage class /// /// /// private string GetMessageNameFromXml(string xmlData) { var doc = new XPathDocument(xmlData); XPathNavigator navigator = doc.CreateNavigator(); var nodeset = navigator.Select("//@Message"); nodeset.MoveNext(); return nodeset.Current.InnerXml; } /// /// if message id can be converted to uint returns the value /// otherwise returns the related label from the message id by dictionary lookup /// /// /// private uint GetLabelFromMessageId(string messageId) { uint labelId = FromStringToUint(messageId); if (labelId == 0) { foreach (var file in _icds) { var item = file.Value.FirstOrDefault(l => l.Value == messageId); if (!string.IsNullOrEmpty(item.Value)) return item.Key; } } return labelId; } /// /// return file path for the file that contains the label /// /// /// private string WhichFileContainsTheLabel(uint label) { foreach (var item in _icds) { if (item.Value.Keys.Contains(label)) { return item.Key; } } return string.Empty; } /// /// reads xml file and extracts all message names with associated labels /// /// /// private Dictionary ProcessFileForNamesAndLabels(string filePath) { var doc = new XPathDocument(filePath); XPathNavigator node = doc.CreateNavigator(); XPathNodeIterator nodeset = node.Select("interface/message"); var result = new Dictionary(); while (nodeset.MoveNext()) { var children = nodeset.Current.SelectChildren(XPathNodeType.Element); if (children.Count > 0) { string strName = string.Empty; string strLabel = string.Empty; while (children.MoveNext()) { if (children.Current.Name == "name") { strName = children.Current.Value; if (!string.IsNullOrEmpty(strName)) strName = strName.Trim(); } else if (children.Current.Name == "label") { strLabel = children.Current.Value; if (!string.IsNullOrEmpty(strLabel)) strLabel = strLabel.Trim(); } } uint iLabel = FromStringToUint(strLabel); if (!string.IsNullOrEmpty(strName) && iLabel > 0) { result.Add(iLabel, strName); } } } return result; } /// /// converts from string representation of a label to uint value /// /// /// private uint FromStringToUint(string data) { if(!string.IsNullOrEmpty(data)) { if (data.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase) || data.StartsWith("&H", StringComparison.CurrentCultureIgnoreCase)) { if (uint.TryParse(data.Substring(2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out uint uiValue)) return uiValue; } else { if (uint.TryParse(data, out uint uiValuel)) return uiValuel; } } return 0; } } }