Files
GenericTeProgramLibrary/Source/TSRealLib/Common/Raytheon.Common/COE/MessagingUtilities/MessageXmlDocument.cs
2025-10-24 15:18:11 -07:00

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;
}
}
}