450 lines
15 KiB
C#
450 lines
15 KiB
C#
// **********************************************************************************************************
|
|
// MessageXmlDocument.cs
|
|
// 5/18/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.Generic;
|
|
using System.Diagnostics;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
using NLog;
|
|
|
|
namespace Raytheon.Common.Coe
|
|
{
|
|
public class MessageXmlDocument : XmlDocument
|
|
{
|
|
private readonly ILogger _logger;
|
|
private readonly string _XmlFileName;
|
|
private string BuiltXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><?xml-stylesheet type=\"text/xsl\" ?><interface>";
|
|
|
|
private uint m_MaxMsgSize = 0;
|
|
private readonly List<string> m_IncludeList;
|
|
|
|
public bool LogData { get; set; }
|
|
|
|
private MessageXmlDocument() : base()
|
|
{
|
|
LogData = true;
|
|
}
|
|
|
|
public MessageXmlDocument(string Pathname, bool logData = true) :
|
|
base()
|
|
{
|
|
LogData = logData;
|
|
|
|
_logger = LogManager.GetCurrentClassLogger();
|
|
_XmlFileName = Pathname;
|
|
|
|
m_IncludeList = new List<string>();
|
|
RecurseProcessing(Pathname);
|
|
|
|
BuiltXML = string.Concat(BuiltXML, "</interface>");
|
|
BytePackingXml addPacking = new BytePackingXml(BuiltXML);
|
|
LoadXml(addPacking.OuterXml);
|
|
}
|
|
|
|
public Dictionary<string, string> GetEnumerations(string Type)
|
|
{
|
|
Dictionary<string, string> enumList = new Dictionary<string, string>();
|
|
|
|
// Get XML nodes to parse the XML ICD document
|
|
XPathNavigator Node = CreateNavigator();
|
|
XPathNodeIterator Nodeset = Node.Select("interface/enum/name");
|
|
|
|
while (Nodeset.MoveNext())
|
|
{
|
|
// Find the enumeration with the name of the type
|
|
if (Nodeset.Current.Value.Trim().Equals(Type.Trim()))
|
|
{
|
|
// Find all the enumeration items
|
|
XPathNavigator enumNode = Nodeset.Current.Clone();
|
|
while (enumNode.MoveToNext())
|
|
{
|
|
if (enumNode.Name.Trim().Equals("item") ||
|
|
enumNode.Name.Trim().Equals("enum_item"))
|
|
{
|
|
string name = null;
|
|
string value = null;
|
|
|
|
// Find all name nodes
|
|
XPathNavigator childNode = enumNode.Clone();
|
|
childNode.MoveToFirstChild();
|
|
do
|
|
{
|
|
if (childNode.Name.Trim().Equals("name"))
|
|
{
|
|
name = childNode.Value.Trim();
|
|
}
|
|
else if (childNode.Name.Trim().Equals("item_name"))
|
|
{
|
|
name = childNode.Value.Trim();
|
|
}
|
|
|
|
if (childNode.Name.Trim().Equals("value"))
|
|
{
|
|
value = childNode.Value.Trim();
|
|
}
|
|
|
|
// Once we find the name & value, add it to the list
|
|
if ((name != null) && (value != null))
|
|
{
|
|
enumList.Add(name, value);
|
|
break;
|
|
}
|
|
} while (childNode.MoveToNext());
|
|
}
|
|
}
|
|
|
|
break; // We found the enumeration we wanted
|
|
}
|
|
}
|
|
|
|
return enumList;
|
|
}
|
|
|
|
private void RecurseProcessing(string pathName)
|
|
{
|
|
string directory;
|
|
string IncludePathname;
|
|
XPathNodeIterator nodeset;
|
|
|
|
// Only process each file once
|
|
pathName = pathName.Replace('/', '\\');
|
|
if (m_IncludeList.Contains(pathName))
|
|
{
|
|
return; // This file has already been processed
|
|
}
|
|
else
|
|
{
|
|
m_IncludeList.Add(pathName);
|
|
}
|
|
|
|
if (LogData)
|
|
_logger.Info($"Loading File: {pathName}");
|
|
XPathDocument document = new XPathDocument(pathName);
|
|
XPathNavigator navigator = document.CreateNavigator();
|
|
|
|
// Verify this is a COE XML ICD
|
|
nodeset = navigator.Select("/interface");
|
|
if (nodeset.Count == 0)
|
|
{
|
|
// This is not an XML ICD
|
|
throw new Exception($"Invalid COE XML Format. Unable to process {pathName}" +
|
|
"\nEnsure this is a properly formatted ICD.");
|
|
}
|
|
|
|
nodeset = navigator.Select("/interface/include/file");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
try
|
|
{
|
|
directory = DirectoryOf(pathName);
|
|
}
|
|
catch
|
|
{
|
|
directory = ".\\";
|
|
}
|
|
|
|
IncludePathname = nodeset.Current.Value.Trim();
|
|
if ((!IncludePathname.StartsWith("\\")) && (!IncludePathname.Contains(":")))
|
|
{
|
|
while (IncludePathname.StartsWith("."))
|
|
{
|
|
if ((IncludePathname.StartsWith("..\\")) || (IncludePathname.StartsWith("../")))
|
|
{
|
|
directory = DirectoryOf(directory);
|
|
IncludePathname = IncludePathname.Remove(0, 3);
|
|
}
|
|
else if ((IncludePathname.StartsWith(".\\")) || (IncludePathname.StartsWith("./")))
|
|
{
|
|
IncludePathname = IncludePathname.Remove(0, 2);
|
|
}
|
|
}
|
|
IncludePathname = string.Concat(directory, "\\", IncludePathname);
|
|
}
|
|
|
|
if (Regex.IsMatch(IncludePathname, @"\.xml", RegexOptions.IgnoreCase))
|
|
RecurseProcessing(IncludePathname);
|
|
}
|
|
|
|
nodeset = navigator.Select("/interface/packing|/interface/typedef|/interface/namespace/typedef|" +
|
|
"/interface/constant|/interface/namespace/constant|interface/enum|interface/namespace/enum|" +
|
|
"/interface/structure|/interface/namespace/structure|/interface/message|/interface/namespace/message");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
string item = nodeset.Current.OuterXml;
|
|
int index;
|
|
while ((index = item.IndexOf("<description>")) != -1)
|
|
{
|
|
item = item.Remove(index, item.IndexOf("</description>") + 14 - index);
|
|
}
|
|
while ((index = item.IndexOf("<!--")) != -1)
|
|
{
|
|
item = item.Remove(index, item.IndexOf("-->") + 3 - index);
|
|
}
|
|
while (item.IndexOf("> ") != -1)
|
|
{
|
|
item = item.Replace("> ", ">");
|
|
}
|
|
while (item.IndexOf(" <") != -1)
|
|
{
|
|
item = item.Replace(" <", "<");
|
|
}
|
|
//_logger.Log(LogLevel.Trace, $"Loading Node :\n{item}");
|
|
Thread.Sleep(1);
|
|
BuiltXML = string.Concat(BuiltXML, item);
|
|
}
|
|
}
|
|
|
|
private string DirectoryOf(string Pathname)
|
|
{
|
|
return Pathname.Remove(Pathname.LastIndexOf("\\"));
|
|
}
|
|
|
|
//
|
|
// From the XML document, the definition of a single message can be identified
|
|
// from the Message Name and returned as an XML string.
|
|
//
|
|
public string XmlFileName
|
|
{
|
|
get
|
|
{
|
|
return _XmlFileName;
|
|
}
|
|
}
|
|
|
|
public string GetMessage(string messageName)
|
|
{
|
|
string message;
|
|
|
|
messageName = messageName.Trim();
|
|
|
|
if (LogData)
|
|
_logger.Info($"Searching for message : {messageName}");
|
|
try
|
|
{
|
|
message = SelectSingleNode($"/interface/message[name='{messageName}']").OuterXml;
|
|
message = TranslateValue(message);
|
|
|
|
string labelStr = Regex.Replace(message, @".+<label>([\d]+)[^\d]+", "$1", RegexOptions.IgnoreCase);
|
|
|
|
int label = 0;
|
|
|
|
Parse.Try(labelStr, out label);
|
|
|
|
if (LogData)
|
|
_logger.Trace($"Found by name: {Regex.Replace(message, @"<label>[\d]+", $"<label>0x{label.ToString("X8")}")}");
|
|
}
|
|
catch
|
|
{
|
|
message = null;
|
|
throw new Exception("Message not found");
|
|
}
|
|
return message;
|
|
}
|
|
|
|
//
|
|
// From the XML document, the definition of a single message can be identified
|
|
// from the Message Label and returned as an XML string.
|
|
//
|
|
public string GetMessageFromLabel(string messageLabel)
|
|
{
|
|
string message;
|
|
|
|
int label = 0;
|
|
|
|
if (Parse.Try(messageLabel, out label) == true)
|
|
{
|
|
if (LogData)
|
|
_logger.Debug($"Searching for message: 0x{label.ToString("X8")}");
|
|
|
|
// Search by message label
|
|
message = SelectSingleNode($"/interface/message[label='{label.ToString()}']").OuterXml;
|
|
message = TranslateValue(message);
|
|
|
|
if (LogData)
|
|
_logger.Debug($"Found by label: {Regex.Replace(message, @"<label>[\d]+", $"<label>0x{label.ToString("X8")}")}");
|
|
}
|
|
else
|
|
{
|
|
if (LogData)
|
|
_logger.Debug($"Searching for message: {messageLabel}");
|
|
|
|
// Search by instruLabel
|
|
message = SelectSingleNode($"/interface/message[instruLabel='{messageLabel}']").OuterXml;
|
|
message = TranslateValue(message);
|
|
|
|
string labelStr = Regex.Replace(message, @".+<label>([\d]+)[^\d]+", "$1", RegexOptions.IgnoreCase);
|
|
|
|
Parse.Try(labelStr, out label);
|
|
|
|
if (LogData)
|
|
_logger.Debug($"Found by name: {Regex.Replace(message, @"<label>[\d]+", $"<label>0x{label.ToString("X8")}")}");
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
//
|
|
// From the XML document, the definition of a single message can be identified
|
|
// from the Message InstruLabel and returned as an XML string.
|
|
//
|
|
public string GetMessageFromInstruLabel(string messageInstruLabel)
|
|
{
|
|
string message;
|
|
|
|
messageInstruLabel = messageInstruLabel.Trim();
|
|
if (LogData)
|
|
_logger.Debug($"Searching for message: {messageInstruLabel}");
|
|
try
|
|
{
|
|
message = SelectSingleNode($"/interface/message[instruLabel='{messageInstruLabel}']").OuterXml;
|
|
message = TranslateValue(message);
|
|
|
|
if (LogData)
|
|
_logger.Debug($"Found by instrument label: {message}");
|
|
}
|
|
catch
|
|
{
|
|
message = null;
|
|
throw new Exception("Message not found");
|
|
}
|
|
return message;
|
|
}
|
|
|
|
public uint GetLargestMessageSize()
|
|
{
|
|
lock (this)
|
|
{
|
|
// return the max message size if we have already calculated it
|
|
if (m_MaxMsgSize != 0)
|
|
{
|
|
return m_MaxMsgSize;
|
|
}
|
|
else
|
|
{
|
|
DateTime t1 = DateTime.Now;
|
|
|
|
XPathNavigator navigator = CreateNavigator();
|
|
XPathNodeIterator nodeset = navigator.Select("/interface/message/name");
|
|
|
|
while (nodeset.MoveNext())
|
|
{
|
|
Message msg = new Message(nodeset.Current.Value.Trim(), this);
|
|
uint msgSize = msg.GetMessageSize();
|
|
if (msgSize > m_MaxMsgSize)
|
|
{
|
|
m_MaxMsgSize = msgSize;
|
|
}
|
|
}
|
|
|
|
DateTime t2 = DateTime.Now;
|
|
TimeSpan duration = t2 - t1;
|
|
Debug.WriteLine("Max Msg Size Algorithm Time = " + duration);
|
|
}
|
|
}
|
|
|
|
return m_MaxMsgSize;
|
|
}
|
|
|
|
public uint GetMessageSize(string MsgName)
|
|
{
|
|
uint msg_size = 0;
|
|
|
|
lock (this)
|
|
{
|
|
XPathNavigator navigator = CreateNavigator();
|
|
XPathNodeIterator nodeset = navigator.Select("/interface/message/name");
|
|
|
|
while (nodeset.MoveNext())
|
|
{
|
|
if (MsgName == nodeset.Current.Value.Trim())
|
|
{
|
|
Message msg = new Message(nodeset.Current.Value.Trim(), this);
|
|
msg_size = msg.GetMessageSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
return msg_size;
|
|
}
|
|
|
|
//
|
|
// Since the XML message definitions contain the definitions of all the enumerations and constants,
|
|
// this object is the only one containing the knowledge to interpret strings using enumerations and/or
|
|
// constants. This method will substitute enumerations and constants with their respective base values
|
|
// in a specified string.
|
|
//
|
|
public string TranslateValue(string Value)
|
|
{
|
|
XPathNavigator navigator = CreateNavigator();
|
|
XPathNavigator position;
|
|
string NewValue = Value;
|
|
|
|
//
|
|
// Substitute enumeration
|
|
//
|
|
try
|
|
{
|
|
position = navigator.SelectSingleNode("/interface/enum/item[name='" + NewValue + "']");
|
|
if (position == null)
|
|
{
|
|
position = navigator.SelectSingleNode("/interface/enum/item[item_name='" + NewValue + "']");
|
|
}
|
|
if (position != null)
|
|
{
|
|
position.MoveToChild("value", "");
|
|
NewValue = position.Value;
|
|
}
|
|
|
|
//
|
|
// Substitute constants
|
|
//
|
|
position = navigator.SelectSingleNode("/interface/constant[name='" + NewValue + "']");
|
|
if (position != null)
|
|
{
|
|
NewValue = position.Value;
|
|
_logger.Debug("Translating field value : " + Value + " -> " + NewValue);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.Error(e.Message);
|
|
}
|
|
|
|
return NewValue;
|
|
}
|
|
|
|
}
|
|
}
|