1248 lines
53 KiB
C#
1248 lines
53 KiB
C#
// **********************************************************************************************************
|
|
// BITCOEDeviceInstrument.cs
|
|
// 6/21/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 System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
using System.Xml.XPath;
|
|
using NLog;
|
|
using Raytheon.Common;
|
|
using Raytheon.Common.Coe;
|
|
using Raytheon.Instruments.Exceptions;
|
|
using static Raytheon.Common.Coe.Message;
|
|
|
|
namespace Raytheon.Instruments
|
|
{
|
|
/// <summary>
|
|
/// This device supports different ways of communicating with other COE nodes
|
|
/// TCP
|
|
/// UDP
|
|
/// Serial
|
|
/// </summary>
|
|
public enum CoeDriverType
|
|
{
|
|
Undefined,
|
|
TCP,
|
|
UDP,
|
|
Serial
|
|
};
|
|
|
|
/// <summary>
|
|
/// Implementation of the IBit interface
|
|
/// </summary>
|
|
public class CoeCommDevice : CoeComm
|
|
{
|
|
private readonly ILogger _logger;
|
|
|
|
private readonly IConfigurationManager _configurationManager;
|
|
private readonly IConfiguration _configuration;
|
|
|
|
/// <summary>
|
|
/// reference to the main wrapper class for Common Operating Environment
|
|
/// </summary>
|
|
private readonly coe _coe;
|
|
|
|
/// <summary>
|
|
/// COE endpoint
|
|
/// </summary>
|
|
private coeEndpoint _endpoint;
|
|
|
|
/// <summary>
|
|
/// cancellation token for stopping reading thread
|
|
/// </summary>
|
|
private CancellationTokenSource _cancellationTokenSource = null;
|
|
|
|
/// <summary>
|
|
/// collection of the messages received
|
|
/// </summary>
|
|
private Dictionary<string, ConcurrentQueue<Tuple<DateTime, OeMessage>>> _messages;
|
|
|
|
// Record the time of when a message was logged, so we can slow down logging
|
|
// For high-rate message, if we don't slow down logging, it will starve other threads from logging
|
|
// and therefore lose logging data
|
|
private Dictionary<string, DateTime?> _messageLogTime = new Dictionary<string, DateTime?>();
|
|
// Keep track of the log interval in seconds for each message
|
|
private Dictionary<string, int> _messageLogIntervalInSec = new Dictionary<string, int>();
|
|
// Keep track whether or not to log for each message
|
|
private Dictionary<string, bool> _messageLogging = new Dictionary<string, bool>();
|
|
|
|
// Record the time of when "Message Received" was logged
|
|
private DateTime? _messageReceivedLogTime = null;
|
|
// Keep track of the log interval in seconds for "Message Received" notification
|
|
private int _messageReceivedLogIntervalInSec = 0;
|
|
|
|
/// <summary>
|
|
/// UDP, TCP, Serial or Undefined
|
|
/// </summary>
|
|
private CoeDriverType _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>
|
|
/// used for initialization of the endpoint
|
|
/// </summary>
|
|
private uint _maxMessageSize;
|
|
private uint _epQueueDepth;
|
|
|
|
/// <summary>
|
|
/// Number of milliseconds to wake up and check the message when receiving
|
|
/// </summary>
|
|
private int _checkForMessageIntervalMs;
|
|
|
|
/// <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>
|
|
/// collection of response labels or messages that COE endpoint should be registered for
|
|
/// </summary>
|
|
private readonly 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>
|
|
/// when set to true the instrument will check every value and if empty
|
|
/// will populate it with the default value
|
|
/// </summary>
|
|
private bool _alwaysSendDefaults;
|
|
|
|
private int _maxQueueSizeForEachMessage = 100;
|
|
|
|
/// <summary>
|
|
/// instrument constructor
|
|
/// </summary>
|
|
public CoeCommDevice(string deviceName, IConfigurationManager configurationManager)
|
|
{
|
|
Info = new InstrumentMetadata
|
|
{
|
|
ModelNumber = "COECommDevice"
|
|
};
|
|
|
|
_logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}");
|
|
|
|
Status = State.Uninitialized;
|
|
DetailedStatus = "COE Uninitialized";
|
|
|
|
Name = deviceName;
|
|
|
|
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<string, ConcurrentQueue<Tuple<DateTime, OeMessage>>>();
|
|
|
|
_coe = new coe();
|
|
}
|
|
|
|
~CoeCommDevice()
|
|
{
|
|
_logger?.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() ...");
|
|
}
|
|
|
|
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()
|
|
{
|
|
_driverType = _configuration.GetConfigurationValue<CoeDriverType>("Parameters", "DriverType");
|
|
|
|
_logger.Debug($"{Name}({_driverType}) Initializing...");
|
|
|
|
_alwaysSendDefaults = _configuration.GetConfigurationValue<bool>("Parameters", "AlwaysSendDefaults");
|
|
|
|
_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")) },
|
|
{ new KeyValuePair<string, string>("TRANSMIT_OPTIONS", _configuration.GetConfigurationValue<string>("ROUTER_CONFIG", "TRANSMIT_OPTIONS", "0")) },
|
|
});
|
|
|
|
_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 CoeDriverType.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 CoeDriverType.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 CoeDriverType.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");
|
|
_epQueueDepth = _configuration.GetConfigurationValue<uint>("Parameters", "EPQueueDepth");
|
|
_checkForMessageIntervalMs = _configuration.GetConfigurationValue<int>("Parameters", "CheckForMessageIntervalMs");
|
|
|
|
RaytheonXmlConfigurationWrapper rayConfigWrapper;
|
|
|
|
rayConfigWrapper = new RaytheonXmlConfigurationWrapper(_configuration.GetXmlConfiguration("MaxQueueSizePerResponseMsg"));
|
|
int.TryParse(rayConfigWrapper.GetValue("/"), out _maxQueueSizeForEachMessage);
|
|
|
|
rayConfigWrapper = new RaytheonXmlConfigurationWrapper(_configuration.GetXmlConfiguration("IncomingMessageNotificationIntervalInSec"));
|
|
int.TryParse(rayConfigWrapper.GetValue("/"), out _messageReceivedLogIntervalInSec);
|
|
|
|
var responseLabels = _configuration.GetConfigurationListValue("ResponseMessageIds", "ResponseLabel", new List<string> { "1", "2" });
|
|
|
|
var coeXmlFiles = _configuration.GetConfigurationListValue("CoeXmlFiles", "FilePath", new List<string> { "File1", "File2" });
|
|
|
|
_xmlDocs.Clear();
|
|
foreach (var file in coeXmlFiles)
|
|
{
|
|
string fileFullPath = Path.Combine(_configurationManager.ConfigurationStoragePath, file);
|
|
fileFullPath = Path.GetFullPath(fileFullPath);
|
|
|
|
_xmlDocs.Add(fileFullPath, new MessageXmlDocument(fileFullPath));
|
|
}
|
|
|
|
_icds.Clear();
|
|
foreach (var file in coeXmlFiles)
|
|
{
|
|
string fileFullPath = Path.Combine(_configurationManager.ConfigurationStoragePath, file);
|
|
fileFullPath = Path.GetFullPath(fileFullPath);
|
|
|
|
_icds.Add(fileFullPath, ProcessFileForNamesAndLabels(fileFullPath));
|
|
}
|
|
|
|
foreach (var strLabel in responseLabels)
|
|
{
|
|
uint label = GetLabelFromMessageId(strLabel);
|
|
if (label > 0)
|
|
_responseLabels.Add(label);
|
|
}
|
|
|
|
rayConfigWrapper = new RaytheonXmlConfigurationWrapper(_configuration.GetXmlConfiguration("MessageLogOptions"));
|
|
XNode xNode = rayConfigWrapper.GetXElement("/Message/");
|
|
while (xNode != null)
|
|
{
|
|
if (xNode.NodeType != System.Xml.XmlNodeType.Element)
|
|
{
|
|
xNode = xNode.NextNode;
|
|
continue;
|
|
}
|
|
|
|
string attrName = "LogIntervalInSec";
|
|
uint label = GetLabelFromMessageId(((XElement)xNode).Value);
|
|
if (((XElement)xNode).Attribute(attrName) != null)
|
|
{
|
|
if (int.TryParse(((XElement)xNode).Attribute(attrName).Value, out int numSecs))
|
|
{
|
|
if (!_messageLogTime.ContainsKey(label.ToString()))
|
|
{
|
|
_messageLogTime[label.ToString()] = null;
|
|
_messageLogIntervalInSec[label.ToString()] = numSecs;
|
|
}
|
|
}
|
|
}
|
|
|
|
attrName = "Log";
|
|
if (((XElement)xNode).Attribute(attrName) != null)
|
|
{
|
|
if (bool.TryParse(((XElement)xNode).Attribute(attrName).Value, out bool logMessage))
|
|
{
|
|
if (!_messageLogging.ContainsKey(label.ToString()))
|
|
_messageLogging[label.ToString()] = logMessage;
|
|
}
|
|
}
|
|
|
|
xNode = xNode.NextNode;
|
|
}
|
|
|
|
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.Debug($"{Name}({_driverType}) Shutting Down...");
|
|
try
|
|
{
|
|
Close();
|
|
//coe.UnloadImportedDll("coeWindows-shared.dll");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, $"{Name}({_driverType}) Error while closing");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens COE connection
|
|
/// </summary>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public void Open()
|
|
{
|
|
_logger.Debug($"{Name}({_driverType}) Opening...");
|
|
|
|
try
|
|
{
|
|
switch (_driverType)
|
|
{
|
|
case CoeDriverType.TCP:
|
|
|
|
if (_coe.tcp_media_binding_configure(_options, _logger) != coe.Status.SUCCESS)
|
|
{
|
|
Status = State.CommunicationFailure;
|
|
throw new CoeNotConnectedException($"{Name}({_driverType}) COE TCP media binding initialization failure. Try checking your connection configuration. You could have a port collision.");
|
|
}
|
|
_logger.Info($"{Name}({_driverType}) COE TCP media binding initialization, {_coe.ProtocolCmitName}");
|
|
break;
|
|
case CoeDriverType.UDP:
|
|
if (_coe.udp_media_binding_configure(_options, _logger) != coe.Status.SUCCESS)
|
|
{
|
|
Status = State.CommunicationFailure;
|
|
throw new CoeNotConnectedException($"{Name}({_driverType}) COE UDP media binding initialization failure. Try checking your connection configuration. You could have a port collision.");
|
|
}
|
|
_logger.Info($"{Name}({_driverType}) COE UDP media binding initialization, Local: {_coe.ProtocolCmitName} Remote: {_coe.ProtocolName}");
|
|
break;
|
|
case CoeDriverType.Serial:
|
|
if (_coe.serial_media_binding_configure(_options, _logger) != coe.Status.SUCCESS)
|
|
{
|
|
Status = State.CommunicationFailure;
|
|
throw new CoeNotConnectedException($"{Name}({_driverType}) COE Serial media binding initialization failure. Try checking your connection configuration. You could have a port collision.");
|
|
}
|
|
_logger.Info($"{Name}({_driverType}) COE Serial media binding initialization, {_coe.ProtocolCmitName}");
|
|
break;
|
|
default:
|
|
throw new CoeNotConnectedException($"{Name}({_driverType}) Configured driver type not valid");
|
|
}
|
|
|
|
foreach (var item in _options)
|
|
{
|
|
int keyMaxCharLength = 40;
|
|
foreach (var pair in item.Value)
|
|
{
|
|
int keyCharPadding = keyMaxCharLength - pair.Key.Length;
|
|
string keyStr = pair.Key;
|
|
if (keyCharPadding > 0)
|
|
{
|
|
for (int i = 0; i < keyCharPadding; i++)
|
|
{
|
|
keyStr += "_";
|
|
}
|
|
}
|
|
|
|
_logger.Info($"{keyStr}{pair.Value}");
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (Exception)
|
|
{
|
|
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.Info($"{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));
|
|
|
|
Status = State.Ready;
|
|
DetailedStatus = "Opened";
|
|
}
|
|
catch (Exception)
|
|
{
|
|
Status = State.CommunicationFailure;
|
|
DetailedStatus = "Unable to Open";
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close COE endpoint
|
|
/// </summary>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public void Close()
|
|
{
|
|
_logger.Debug($"{Name}({_driverType}) Closing ...");
|
|
|
|
Status = State.Uninitialized;
|
|
|
|
_cancellationTokenSource?.Cancel();
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
_messages.Clear();
|
|
|
|
_coe.SetConnected(false);
|
|
var status = _driverType == CoeDriverType.TCP ?
|
|
_coe.TCP_media_binding_shutdown(_logger) : _driverType == CoeDriverType.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;
|
|
|
|
DetailedStatus = "Closed";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message
|
|
/// </summary>
|
|
/// <param name="messageId"></param>
|
|
/// <param name="timeoutInMs"></param>
|
|
/// <param name="messageParams"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public bool SendMessage(string messageId, IEnumerable<KeyValuePair<string, string>> messageParams)
|
|
{
|
|
if (!_coe.IsConnected)
|
|
{
|
|
throw new CoeNotConnectedException();
|
|
}
|
|
|
|
if (Status != State.Ready)
|
|
{
|
|
throw new Exception("COE is not initialized");
|
|
}
|
|
|
|
var label = GetLabelFromMessageId(messageId);
|
|
|
|
var coeXmlFile = WhichFileContainsTheLabel(label);
|
|
if (string.IsNullOrEmpty(coeXmlFile))
|
|
{
|
|
var msg = $"Message Id {messageId} not found in any of the COE XML files";
|
|
throw new CoeParseException(msg);
|
|
}
|
|
|
|
try
|
|
{
|
|
bool logData = true;
|
|
if (_messageLogging.ContainsKey(label.ToString()))
|
|
{
|
|
if (!_messageLogging[label.ToString()])
|
|
{
|
|
logData = false;
|
|
}
|
|
}
|
|
|
|
if (logData && _messageLogTime.ContainsKey(label.ToString()))
|
|
{
|
|
if (_messageLogTime[label.ToString()] != null)
|
|
{
|
|
logData = false;
|
|
|
|
double elapsedSecs = DateTime.Now.Subtract((DateTime)_messageLogTime[label.ToString()]).TotalSeconds;
|
|
|
|
if (elapsedSecs >= (double)_messageLogIntervalInSec[label.ToString()])
|
|
{
|
|
logData = true;
|
|
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
|
|
var message = GetOeMessageWithParameters(label, messageParams, coeXmlFile, logData);
|
|
|
|
if (logData)
|
|
{
|
|
_logger.Info("Sending ...");
|
|
message.XmlMessage.SendToLog(EnumerationType.EXPANDED_FIELDS, MessageDirection.Out);
|
|
}
|
|
|
|
var status = _endpoint.Send(message);
|
|
|
|
if (status != coe.Status.SUCCESS)
|
|
{
|
|
_logger.Error($"Error sending COE message, error code: {status}");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get next response in FIFO Queue, meaning we are getting the oldest response
|
|
/// </summary>
|
|
/// <param name="messageId"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public CoeResponseMsgData GetNextResponseInQueue(string messageId)
|
|
{
|
|
if (!_coe.IsConnected)
|
|
{
|
|
throw new CoeNotConnectedException();
|
|
}
|
|
|
|
uint label = 0;
|
|
// empty string or zero means first available message from the top
|
|
if (!string.IsNullOrEmpty(messageId) && messageId != "0")
|
|
{
|
|
label = GetLabelFromMessageId(messageId);
|
|
if (label == 0)
|
|
{
|
|
throw new Exception($"{Name}({_driverType}) Unable to match message {messageId} with anything in the dictionary. Check your configuration");
|
|
}
|
|
}
|
|
|
|
string strLabel = label.ToString();
|
|
|
|
Tuple<DateTime, OeMessage> message = null;
|
|
lock (this)
|
|
{
|
|
ConcurrentQueue<Tuple<DateTime, OeMessage>> queue;
|
|
queue = label == 0 ?
|
|
_messages.Any() ? _messages.FirstOrDefault().Value : null :
|
|
_messages.ContainsKey(strLabel) ? _messages[strLabel] : null;
|
|
|
|
if (queue != null)
|
|
{
|
|
queue.TryDequeue(out message);
|
|
}
|
|
}
|
|
|
|
if (message != null)
|
|
{
|
|
OeMessage oeMessage = message.Item2;
|
|
// make a copy of the buffer
|
|
var xmlMessage = oeMessage.XmlMessage;
|
|
if (xmlMessage != null)
|
|
{
|
|
var results = new CoeResponseMsgData
|
|
{
|
|
Label = oeMessage.Label,
|
|
Time = message.Item1,
|
|
Name = xmlMessage.Name,
|
|
|
|
// parse message result into list of results
|
|
FieldDataList = FromXmlToBitTestResults(xmlMessage)
|
|
};
|
|
|
|
return results;
|
|
}
|
|
else
|
|
{
|
|
var msg = $"Found a message with label {label}, but the Buffer is empty";
|
|
throw new CoeParseException(msg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// message not found
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the queue for a particular response message.
|
|
/// This is useful if we have a large size queue and we don't want to dequeue each item to get
|
|
/// to the newest item. So we clear the queue first, before trying to get the newest item in the queue
|
|
/// </summary>
|
|
public void ClearResponseMessageQueue(string messageId)
|
|
{
|
|
if (!_coe.IsConnected)
|
|
{
|
|
throw new CoeNotConnectedException();
|
|
}
|
|
|
|
uint label = 0;
|
|
// empty string or zero means first available message from the top
|
|
if (!string.IsNullOrEmpty(messageId) && messageId != "0")
|
|
{
|
|
label = GetLabelFromMessageId(messageId);
|
|
if (label == 0)
|
|
{
|
|
throw new Exception($"{Name}({_driverType}) Unable to match message {messageId} with anything in the dictionary. Check your configuration");
|
|
}
|
|
}
|
|
|
|
string strLabel = label.ToString();
|
|
|
|
lock (this)
|
|
{
|
|
if (_messages.ContainsKey(strLabel))
|
|
{
|
|
_messages.Remove(strLabel);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get XML Docs
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public object GetXmlDocs()
|
|
{
|
|
return _xmlDocs;
|
|
}
|
|
|
|
#region Private Functions
|
|
|
|
/// <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.Info($"{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)
|
|
{
|
|
bool logMessageReceived = true;
|
|
if (_messageReceivedLogTime != null)
|
|
{
|
|
logMessageReceived = false;
|
|
|
|
double elapsedSecs = DateTime.Now.Subtract((DateTime)_messageReceivedLogTime).TotalSeconds;
|
|
|
|
if (elapsedSecs >= (double)_messageReceivedLogIntervalInSec)
|
|
{
|
|
logMessageReceived = true;
|
|
|
|
_messageReceivedLogTime = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
_messageReceivedLogTime = DateTime.Now;
|
|
|
|
if (logMessageReceived)
|
|
_logger.Debug("Message Received...");
|
|
|
|
bool logData = true;
|
|
while (_endpoint.Peek(out uint label, out uint size, out int priority) == coe.Status.SUCCESS)
|
|
{
|
|
logData = true;
|
|
var hexLabel = $"0x{label:X}";
|
|
|
|
if (_messageLogging.ContainsKey(label.ToString()))
|
|
{
|
|
if (!_messageLogging[label.ToString()])
|
|
{
|
|
logData = false;
|
|
}
|
|
}
|
|
|
|
if (logData && _messageLogTime.ContainsKey(label.ToString()))
|
|
{
|
|
if (_messageLogTime[label.ToString()] != null)
|
|
{
|
|
logData = false;
|
|
|
|
double elapsedSecs = DateTime.Now.Subtract((DateTime)_messageLogTime[label.ToString()]).TotalSeconds;
|
|
|
|
if (elapsedSecs >= (double)_messageLogIntervalInSec[label.ToString()])
|
|
{
|
|
logData = true;
|
|
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
|
|
if (logData)
|
|
_logger.Debug($"{Name}({_driverType}) Identified message by peeking... {label} ({hexLabel})");
|
|
|
|
var xmlDoc = WhichFileContainsTheLabel(label);
|
|
|
|
bool prevLogData = _xmlDocs[xmlDoc].LogData;
|
|
_xmlDocs[xmlDoc].LogData = logData;
|
|
var message = new OeMessage((int)size)
|
|
{
|
|
XmlMessage = new Message(_xmlDocs[xmlDoc], label.ToString(), logData)
|
|
};
|
|
_xmlDocs[xmlDoc].LogData = prevLogData;
|
|
|
|
status = _endpoint.Receive(message);
|
|
|
|
if (status == coe.Status.SUCCESS)
|
|
{
|
|
if (logData)
|
|
{
|
|
_logger.Debug($"{Name}({_driverType}) Successfully read message... Label: {hexLabel} ({message.XmlMessage?.Name})");
|
|
|
|
message.XmlMessage.SendToLog(EnumerationType.EXPANDED_FIELDS, MessageDirection.In);
|
|
}
|
|
|
|
lock (this)
|
|
{
|
|
if (!_messages.ContainsKey(message.Label))
|
|
{
|
|
_messages.Add(message.Label, new ConcurrentQueue<Tuple<DateTime, OeMessage>>());
|
|
}
|
|
|
|
if (_messages[message.Label].Count >= _maxQueueSizeForEachMessage)
|
|
{
|
|
_messages[message.Label].TryDequeue(out _);
|
|
}
|
|
|
|
_messages[message.Label].Enqueue(new Tuple<DateTime, OeMessage>(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.Message + "\r\n" + ex.StackTrace);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine if we should log this message
|
|
/// </summary>
|
|
/// <param name="messageName"></param>
|
|
/// <returns></returns>
|
|
public bool ShallLogMessage(string messageName)
|
|
{
|
|
bool logData = true;
|
|
|
|
uint label = GetLabelFromMessageId(messageName);
|
|
|
|
if (_messageLogging.ContainsKey(label.ToString()))
|
|
{
|
|
if (!_messageLogging[label.ToString()])
|
|
{
|
|
logData = false;
|
|
}
|
|
}
|
|
|
|
if (logData && _messageLogTime.ContainsKey(label.ToString()))
|
|
{
|
|
if (_messageLogTime[label.ToString()] != null)
|
|
{
|
|
logData = false;
|
|
|
|
double elapsedSecs = DateTime.Now.Subtract((DateTime)_messageLogTime[label.ToString()]).TotalSeconds;
|
|
|
|
if (elapsedSecs >= (double)_messageLogIntervalInSec[label.ToString()])
|
|
{
|
|
logData = true;
|
|
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
_messageLogTime[label.ToString()] = DateTime.Now;
|
|
}
|
|
|
|
return logData;
|
|
}
|
|
|
|
/// <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>
|
|
/// convert from Message to list of BItTestResult fields
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <returns></returns>
|
|
private IList<CoeFieldData> FromXmlToBitTestResults(Message message)
|
|
{
|
|
if (message == null)
|
|
return null;
|
|
|
|
var result = FromMessageArrayToBitResult(message.MessageDataArray);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// recursive function for getting results out
|
|
/// </summary>
|
|
/// <param name="messages"></param>
|
|
/// <returns></returns>
|
|
private IList<CoeFieldData> FromMessageArrayToBitResult(MessageData[] messages)
|
|
{
|
|
if (messages == null || messages.Length == 0)
|
|
return null;
|
|
|
|
var result = new List<CoeFieldData>();
|
|
|
|
foreach (var item in messages)
|
|
{
|
|
result.Add(FromMessageDataToBitTestResult(item));
|
|
|
|
if (item.MessageArray != null && item.MessageArray.Length > 0)
|
|
{
|
|
var moreResults = FromMessageArrayToBitResult(item.MessageArray);
|
|
result.AddRange(moreResults);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// copy message data fields to BitTestResult
|
|
/// </summary>
|
|
/// <param name="from"></param>
|
|
/// <returns></returns>
|
|
private static CoeFieldData FromMessageDataToBitTestResult(MessageData from)
|
|
{
|
|
if (from == null)
|
|
return null;
|
|
|
|
return new CoeFieldData
|
|
{
|
|
FieldArrayValue = from.FieldArrayValue,
|
|
FieldBitValue = from.FieldBitValue,
|
|
FieldDefaultValue = from.FieldDefaultValue,
|
|
FieldInstruType = from.FieldInstruType,
|
|
FieldMaxValue = from.FieldMaxValue,
|
|
FieldMinValue = from.FieldMinValue,
|
|
FieldName = from.FieldName,
|
|
FieldType = from.FieldType,
|
|
FieldValue = from.FieldValue,
|
|
Variable = from.Variable,
|
|
MaxOffset = from.MaxOffset,
|
|
MinOffset = from.MinOffset,
|
|
VerifyType = from.VerifyType,
|
|
IsSelected = from.isSelected,
|
|
IsArray = from.isArray,
|
|
IsStructure = from.isStructure,
|
|
IsArrayOfStructures = from.isArrayOfStructures,
|
|
IsEnum = from.isEnum,
|
|
UsesRegister = from.usesRegister,
|
|
IsValid = from.isValid,
|
|
UseRange = from.useRange,
|
|
ArrayLength = from.arrayLength,
|
|
ImageWidth = from.imageWidth,
|
|
ImageHeight = from.imageHeight,
|
|
ImagePixelSize = from.imagePixelSize,
|
|
BitMask = from.bitMask,
|
|
Expanded = from.expanded,
|
|
Depth = from.depth,
|
|
ImageBuffer = from.imageBuffer?.ToArray(),
|
|
ImageBufferSize = from.imageBufferSize,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build OeMessage from messageId and provided parameters
|
|
/// </summary>
|
|
/// <param name="messageId"></param>
|
|
/// <param name="messageParams"></param>
|
|
/// <returns></returns>
|
|
private OeMessage GetOeMessageWithParameters(uint messageId, IEnumerable<KeyValuePair<string, string>> messageParams, string bitFilePath, bool logData = true)
|
|
{
|
|
var messageName = _icds[bitFilePath][messageId];
|
|
var message = new OeMessage(new Message(messageName, new MessageXmlDocument(bitFilePath, logData), logData));
|
|
|
|
if (messageParams != null)
|
|
{
|
|
message.XmlMessage.MessageDataArray = PopulateParameters(message.XmlMessage.MessageDataArray, 0, messageParams);
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
/// <summary>
|
|
/// recursive function to populate parameters
|
|
/// </summary>
|
|
/// <param name="data">message data array</param>
|
|
/// <param name="level">indicates how deep in the tree we are populating parameters</param>
|
|
/// <param name="messageParams">message parameters</param>
|
|
/// <returns></returns>
|
|
internal MessageData[] PopulateParameters(MessageData[] data, int level, IEnumerable<KeyValuePair<string, string>> messageParams)
|
|
{
|
|
// only get parameters from the same level
|
|
var levelParams = messageParams.Where(m => m.Key.Where(c => c == '.' || c == ']').Count() == level);
|
|
|
|
foreach (var item in data)
|
|
{
|
|
if (item.FieldName.StartsWith("$"))
|
|
continue;
|
|
|
|
var messageParam = levelParams.FirstOrDefault(m => m.Key.EndsWith(item.FieldName));
|
|
if (!string.IsNullOrEmpty(messageParam.Key) && !string.IsNullOrEmpty(messageParam.Value))
|
|
{
|
|
item.FieldValue = messageParam.Value;
|
|
}
|
|
// always send defaults means that even if parameter was not provided use the default value to populate field value
|
|
else if (_alwaysSendDefaults)
|
|
{
|
|
item.FieldValue = item.FieldDefaultValue;
|
|
}
|
|
|
|
// if there are more levels, update recursively
|
|
if (item.MessageArray != null && item.MessageArray.Length > 0)
|
|
item.MessageArray = PopulateParameters(item.MessageArray, level + 1, messageParams);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <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|interface/namespace/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;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|