Files
GenericTeProgramLibrary/Source/TSRealLib/HAL/Implementations/BIT/COECommDevice/COECommDeviceInstrument.cs
2025-03-13 12:04:22 -07:00

860 lines
41 KiB
C#

// **********************************************************************************************************
// 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
{
/// <summary>
/// This device supports different ways of communicating with other COE nodes
/// TCP
/// UDP
/// Serial
/// </summary>
public enum DriverType
{
Undefined,
TCP,
UDP,
Serial
};
public class COECommDeviceInstrument : ICommDevice
{
/// <summary>
/// Nlog Logger
/// </summary>
readonly ILogger _logger;
/// <summary>
/// Cancellation token to stop reading incoming messages
/// </summary>
private CancellationTokenSource _cancellationTokenSource = null;
/// <summary>
/// collection of the messages received
/// </summary>
private readonly Dictionary<Tuple<string, DateTime>, OeMessage> _messages;
/// <summary>
/// Raytheon configuration
/// </summary>
private readonly IConfigurationManager _configurationManager;
private readonly IConfiguration _configuration;
/// <summary>
/// UDP, TCP, Serial or Undefined
/// </summary>
private DriverType _driverType;
/// <summary>
/// dictionary of options when initializing COE endpoint and router
/// </summary>
private readonly Dictionary<string, List<KeyValuePair<string, string>>> _options = new Dictionary<string, List<KeyValuePair<string, string>>>();
/// <summary>
/// reference to the main wrapper class for Common Operating Environment
/// </summary>
private readonly coe _coe;
/// <summary>
/// COE endpoint
/// </summary>
private coeEndpoint _endpoint;
/// <summary>
/// used for initialization of the endpoint
/// </summary>
private uint _maxMessageSize;
private uint _epQueueDepth;
/// <summary>
/// collection of all labels with message names per every XML file
/// </summary>
private readonly Dictionary<string, Dictionary<uint, string>> _icds = new Dictionary<string, Dictionary<uint, string>>();
/// <summary>
/// timeout can be set for the reader in a separate call
/// </summary>
private uint _receiverTimeout;
/// <summary>
/// Number of milliseconds to wake up and check the message when receiving
/// </summary>
private int _checkForMessageIntervalMs;
/// <summary>
/// collection of response labels or messages that COE endpoint should be registered for
/// </summary>
private List<uint> _responseLabels = new List<uint>();
/// <summary>
/// collection of message XML documents (processed XML files) used in COE communications
/// </summary>
private readonly Dictionary<string, MessageXmlDocument> _xmlDocs = new Dictionary<string, MessageXmlDocument>();
/// <summary>
/// instrument constructor
/// </summary>
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<Tuple<string, DateTime>, 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;
}
/// <summary>
/// Initializes COE instrument
/// </summary>
public void Initialize()
{
_logger.Trace($"{Name}({_driverType}) Initializing...");
if (_driverType == DriverType.Undefined)
_driverType = _configuration.GetConfigurationValue<DriverType>("Parameters", "DriverType", "TCP");
_options.Clear();
_options.Add("ROUTER_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("NODE_ID", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "NODE_ID", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_STATE", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_STATE", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_LABEL_MESSAGE", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_LABEL_MESSAGE", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_BRIDGE_REGISTRATION", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_BRIDGE_REGISTRATION", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_ROUTER_DATABASE", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_ROUTER_DATABASE", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "DISPLAY_DEBUG_RECV", "0")) },
{ new KeyValuePair<string, string>("BUFFER_SIZE", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "BUFFER_SIZE", "256")) },
{ new KeyValuePair<string, string>("ENABLE_REGISTRATION_MESSAGES", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "ENABLE_REGISTRATION_MESSAGES", "1")) },
{ new KeyValuePair<string, string>("THREAD_STACK_SIZE", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "THREAD_STACK_SIZE", "16384")) },
});
_options.Add("ROUTER_PROTOCOL_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("ROUTER_PROTOCOL_CONFIG", "DISPLAY_DEBUG_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("ROUTER_PROTOCOL_CONFIG", "DISPLAY_DEBUG_RECV", "0")) },
{ new KeyValuePair<string, string>("THREAD_STACK_SIZE", _configuration.GetConfigurationValue<string>("ROUTER_PROTOCOL_CONFIG", "THREAD_STACK_SIZE", "16384")) },
});
var poolEntry = _configuration.GetConfigurationValue<string>("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<KeyValuePair<string, string>>();
foreach (var entry in poolEntries)
{
entries.Add(new KeyValuePair<string, string>("POOL_ENTRY", entry));
}
_options.Add("ROUTER_BUFFER_POOLS", entries);
}
}
_options.Add("BASIC_REGISTRATION_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND_BUFFER", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_SEND_BUFFER", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_RECV", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV_BUFFER", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_RECV_BUFFER", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_STATE", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_STATE", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_PING_SEND", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_PING_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_PING_RECV", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "DISPLAY_DEBUG_PING_RECV", "0")) },
{ new KeyValuePair<string, string>("THREAD_STACK_SIZE", _configuration.GetConfigurationValue<string>("BASIC_REGISTRATION_CONFIG", "THREAD_STACK_SIZE", "16384")) },
});
switch (_driverType)
{
case DriverType.UDP:
_options.Add("UDP_MEDIA_BINDING_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("LOCAL_IP_ADDRESS", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "LOCAL_IP_ADDRESS", "127.0.0.1")) },
{ new KeyValuePair<string, string>("REMOTE_IP_ADDRESS", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "REMOTE_IP_ADDRESS", "127.0.0.1")) },
{ new KeyValuePair<string, string>("LOCAL_SEND_PORT", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "LOCAL_SEND_PORT", "32010")) },
{ new KeyValuePair<string, string>("LOCAL_RECV_PORT", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "LOCAL_RECV_PORT", "32020")) },
{ new KeyValuePair<string, string>("REMOTE_SEND_PORT", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "REMOTE_SEND_PORT", "32011")) },
{ new KeyValuePair<string, string>("REMOTE_RECV_PORT", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "REMOTE_RECV_PORT", "32021")) },
{ new KeyValuePair<string, string>("RECV_TIMEOUT", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "RECV_TIMEOUT", "200")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "0")) },
{ new KeyValuePair<string, string>("MTU_SIZE", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "MTU_SIZE", "1472")) },
{ new KeyValuePair<string, string>("THREAD_STACK_SIZE", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "THREAD_STACK_SIZE", "16384")) },
{ new KeyValuePair<string, string>("THREAD_NAME", _configuration.GetConfigurationValue<string>("UDP_MEDIA_BINDING_CONFIG", "THREAD_NAME", "UDP_MB_RCV")) },
});
break;
case DriverType.TCP:
_options.Add("TCP_MEDIA_BINDING_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("LOCAL_PORT", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "LOCAL_PORT", "9990")) },
{ new KeyValuePair<string, string>("NUM_PORTS", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "NUM_PORTS", "32")) },
{ new KeyValuePair<string, string>("NUM_DYNAMIC_NODES", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "NUM_DYNAMIC_NODES", "32")) },
{ new KeyValuePair<string, string>("SERVER_ADDRESS", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "SERVER_ADDRESS", "127.0.0.1:9990")) },
{ new KeyValuePair<string, string>("UDP_TX_BUFFER_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "UDP_TX_BUFFER_SIZE", "5000")) },
{ new KeyValuePair<string, string>("UDP_RX_BUFFER_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "UDP_RX_BUFFER_SIZE", "32768")) },
{ new KeyValuePair<string, string>("TCP_TX_BUFFER_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "TCP_TX_BUFFER_SIZE", "5000")) },
{ new KeyValuePair<string, string>("TCP_RX_BUFFER_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "TCP_RX_BUFFER_SIZE", "4096")) },
{ new KeyValuePair<string, string>("PACKET_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "PACKET_SIZE", "5128")) },
{ new KeyValuePair<string, string>("TCP_SELECT_VALUE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "TCP_SELECT_VALUE", "1")) },
{ new KeyValuePair<string, string>("DISABLE_NAG_DELAY", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISABLE_NAG_DELAY", "1")) },
{ new KeyValuePair<string, string>("TIMER_RATE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "TIMER_RATE", "1000")) },
{ new KeyValuePair<string, string>("CONNECT_KA_RATE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "CONNECT_KA_RATE", "1")) },
{ new KeyValuePair<string, string>("RECV_KA_RATE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "RECV_KA_RATE", "1")) },
{ new KeyValuePair<string, string>("SERVER_CONNECT_RATE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "SERVER_CONNECT_RATE", "1")) },
{ new KeyValuePair<string, string>("RECV_THREAD_STACK_SIZE", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_STACK_SIZE", "4096")) },
{ new KeyValuePair<string, string>("RECV_THREAD_PRIORITY", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_PRIORITY", "0")) },
{ new KeyValuePair<string, string>("RECV_THREAD_AFFINITY", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_AFFINITY", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND_BUFFER", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND_BUFFER", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_UDP_RECV", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_UDP_RECV", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_UDP_RECV_BUFFER", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_UDP_RECV_BUFFER", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_TCP_RECV", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_TCP_RECV", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_TCP_RECV_BUFFER", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_TCP_RECV_BUFFER", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "1")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV_BUFFER", _configuration.GetConfigurationValue<string>("TCP_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV_BUFFER", "1")) },
});
break;
case DriverType.Serial:
_options.Add("SERIAL_MEDIA_BINDING_CONFIG", new List<KeyValuePair<string, string>>
{
{ new KeyValuePair<string, string>("DEVICE_NAME", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "DEVICE_NAME", "\\\\.\\COM1")) },
{ new KeyValuePair<string, string>("BAUD_RATE", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "BAUD_RATE", "9600")) },
{ new KeyValuePair<string, string>("DATA_BITS", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "DATA_BITS", "8")) },
{ new KeyValuePair<string, string>("STOP_BITS", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "STOP_BITS", "1")) },
{ new KeyValuePair<string, string>("PARITY", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "PARITY", "0")) },
{ new KeyValuePair<string, string>("FLOW_CONTROL", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "FLOW_CONTROL", "0")) },
{ new KeyValuePair<string, string>("MTU_SIZE", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "MTU_SIZE", "256")) },
{ new KeyValuePair<string, string>("RECV_PROCESSING_DELAY", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "RECV_PROCESSING_DELAY", "100")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_SEND", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_SEND", "0")) },
{ new KeyValuePair<string, string>("DISPLAY_DEBUG_RECV", _configuration.GetConfigurationValue<string>("SERIAL_MEDIA_BINDING_CONFIG", "DISPLAY_DEBUG_RECV", "0")) },
});
break;
default:
_logger.Error($"{Name}({_driverType}) Configured driver type not valid");
break;
}
_maxMessageSize = _configuration.GetConfigurationValue<uint>("Parameters", "MaxMessageSize", "5000");
_epQueueDepth = _configuration.GetConfigurationValue<uint>("Parameters", "EPQueueDepth", "5000");
_checkForMessageIntervalMs = _configuration.GetConfigurationValue<int>("Parameters", "CheckForMessageIntervalMs", "100");
var responseLabels = _configuration.GetConfigurationListValue("ResponseMessageIds", "ResponseLabel", new List<string> { "1", "2" });
var bitFilePaths = _configuration.GetConfigurationListValue("BitFilePaths", "FilePath", new List<string> { "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;
}
/// <summary>
/// performs self-test
/// </summary>
/// <returns></returns>
public SelfTestResult PerformSelfTest()
{
_logger.Trace($"{Name}({_driverType}) Performing Self Test...");
// TODO implement method
return SelfTestResult.Pass;
}
/// <summary>
/// Resets COE device comms
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void Reset()
{
_logger.Trace($"{Name}({_driverType}) Resetting...");
Close();
Open();
}
/// <summary>
/// Shuts down COE device
/// </summary>
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
/// <summary>
/// Opens COE connection
/// </summary>
/// <exception cref="NotImplementedException"></exception>
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;
}
}
/// <summary>
/// Close COE endpoint
/// </summary>
/// <exception cref="NotImplementedException"></exception>
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";
}
/// <summary>
/// sets the timeout value for COE reading operation
/// </summary>
/// <param name="timeout"></param>
/// <exception cref="NotImplementedException"></exception>
public void SetReadTimeout(uint timeout)
{
_logger.Trace($"{Name}({_driverType}) Setting read timeout {timeout} ms ...");
_receiverTimeout = timeout;
}
/// <summary>
/// reads COE message either based on the label or just top message
/// </summary>
/// <param name="dataRead">
/// might contain a COE label
/// </param>
/// <returns></returns>
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<Tuple<string, DateTime>, 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;
}
/// <summary>
/// keep reading messages and stash them in _messages dictionary with label and timestamp as a key
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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<string, DateTime>(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);
}
}
/// <summary>
/// Writes COE message
/// </summary>
/// <param name="data"></param>
/// <param name="numBytesToWrite"></param>
/// <returns></returns>
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
/// <summary>
/// if byte array contains byte representation of XML then returns XML string
/// else returns empty string
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static string FromByteArrayToXml(byte[] data) => System.Text.Encoding.Default.GetString(data);
private static byte[] FromStringToByteArray(string data) => System.Text.Encoding.UTF8.GetBytes(data);
/// <summary>
/// Extract message node from XML for the OeMessage class
/// </summary>
/// <param name="xmlData"></param>
/// <returns></returns>
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;
}
/// <summary>
/// if message id can be converted to uint returns the value
/// otherwise returns the related label from the message id by dictionary lookup
/// </summary>
/// <param name="label"></param>
/// <returns></returns>
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;
}
/// <summary>
/// return file path for the file that contains the label
/// </summary>
/// <param name="label"></param>
/// <returns></returns>
private string WhichFileContainsTheLabel(uint label)
{
foreach (var item in _icds)
{
if (item.Value.Keys.Contains(label))
{
return item.Key;
}
}
return string.Empty;
}
/// <summary>
/// reads xml file and extracts all message names with associated labels
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private Dictionary<uint, string> ProcessFileForNamesAndLabels(string filePath)
{
var doc = new XPathDocument(filePath);
XPathNavigator node = doc.CreateNavigator();
XPathNodeIterator nodeset = node.Select("interface/message");
var result = new Dictionary<uint, string>();
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;
}
/// <summary>
/// converts from string representation of a label to uint value
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
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;
}
}
}