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

503 lines
17 KiB
C#

// **********************************************************************************************************
// BytePackingXml.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.Xml;
using System.Xml.XPath;
namespace Raytheon.Instruments.MessagingUtilities
{
// Takes an XML ICD and adds in nodes to represent the byte packing
// The padding added will be displayed in the tool as arrays of characters
internal class BytePackingXml : XmlDocument
{
private readonly int m_Packing = 8;
private readonly Dictionary<string, double> m_ConstMap;
private readonly Dictionary<string, int> m_SizeMap;
private readonly Dictionary<string, int> m_PadMap;
public BytePackingXml(string icdStr) : base()
{
// Create the dictionaries
m_ConstMap = new Dictionary<string, double>();
m_PadMap = new Dictionary<string, int>();
m_SizeMap = new Dictionary<string, int>();
// Create an XML document from the string
LoadXml(icdStr);
XPathNavigator node = CreateNavigator();
//Get the type of packing
XPathNodeIterator nodeset = node.Select("/interface/packing");
if (nodeset.MoveNext() == true)
{
if (!Parse.Try(nodeset.Current.Value.Trim(), out m_Packing))
{
switch (nodeset.Current.Value.Trim())
{
case "char":
case "1":
m_Packing = 1;
break;
case "short":
case "2":
m_Packing = 2;
break;
case "long":
case "4":
m_Packing = 4;
break;
case "double":
case "8":
default:
m_Packing = 8;
break;
}
}
}
// Handle all of the constants
nodeset = node.Select("/interface/constant");
while (nodeset.MoveNext())
{
ProcessConstantNode(nodeset.Current);
}
nodeset = node.Select("/interface/typedef");
while (nodeset.MoveNext())
{
ProcessTypedefNode(nodeset.Current);
}
nodeset = node.Select("/interface/enum");
while (nodeset.MoveNext())
{
ProcessEnumerationNode(nodeset.Current);
}
nodeset = node.Select("/interface/structure|/interface/message");
while (nodeset.MoveNext())
{
ProcessStructureNode(nodeset.Current);
}
NormalizeIcdLabels();
}
// This function takes all of the messages in the ICD
// and converts the labels into decimal, so when we do
// a string lookup, they will all be in the same known format
private void NormalizeIcdLabels()
{
XPathNavigator navigator = CreateNavigator();
XPathNodeIterator nodeset;
nodeset = navigator.Select("/interface/message/label");
while (nodeset.MoveNext())
{
try
{
double dLabel = GetConstFromString(nodeset.Current.Value);
nodeset.Current.SetValue(((int)dLabel).ToString());
}
catch
{
throw new Exception("Message Label, " + nodeset.Current.Value + ", can not be converted to an integer");
}
}
}
private void ProcessConstantNode(XPathNavigator node)
{
string name = "";
string constStr = "";
double constNum = 0;
if (node.MoveToChild("name", ""))
{
name = node.Value.Trim();
node.MoveToParent();
}
if (node.MoveToChild("value", ""))
{
constStr = node.Value.Trim();
if ((constStr.Length != 0) && (constStr[0] != '\"'))
{
constNum = GetConstFromString(constStr);
}
else
{
constNum = 0;
}
node.MoveToParent();
}
// Verify the correctnes of the <constant> tag
if ((name != "") && (constStr != ""))
{
AddItemToMap(m_ConstMap, name, constNum);
}
else
{
throw new Exception(
"ERROR: Constant Definition Incorrect - <name>:" + name +
" <value>:" + constStr);
}
}
private void ProcessTypedefNode(XPathNavigator node)
{
string name = "";
string type = "";
int typeSize = 0; // Size of the item
int typePad = 0; //Size of the largest item to pad to
if (node.MoveToChild("name", ""))
{
name = node.Value.Trim();
node.MoveToParent();
}
if (node.MoveToChild("value", ""))
{
type = node.Value.Trim();
GetSizeFromType(type, out typeSize, out typePad);
node.MoveToParent();
}
// Verify the correctnes of the <typedef> tag
if ((name != "") && (type != ""))
{
AddItemToMap(m_PadMap, name, typePad);
AddItemToMap(m_SizeMap, name, typeSize);
}
else
{
throw new Exception(
"ERROR: Typedef Definition Incorrect - <name>:" + name +
" <value>:" + type);
}
}
private void ProcessEnumerationNode(XPathNavigator node)
{
string name;
double constNum = 0;
var constStr = string.Empty;
if (node.MoveToChild("name", ""))
{
name = node.Value.Trim();
AddItemToMap(m_PadMap, name, 4);
AddItemToMap(m_SizeMap, name, 4);
node.MoveToParent();
}
else
{
throw new Exception("ERROR: Enumeration Definition Incorrect. No <name> tag present.");
}
XPathNodeIterator nodeSet = node.Select("item|enum_item");
while (nodeSet.MoveNext())
{
name = string.Empty;
if ((nodeSet.Current.MoveToChild("name", "")) ||
(nodeSet.Current.MoveToChild("item_name", "")))
{
name = nodeSet.Current.Value.Trim();
nodeSet.Current.MoveToParent();
}
if (nodeSet.Current.MoveToChild("value", ""))
{
constStr = nodeSet.Current.Value.Trim();
constNum = GetConstFromString(constStr);
nodeSet.Current.MoveToParent();
}
// Verify the correctnes of the <enum><item> tag
if ((name != "") && (constStr != ""))
{
AddItemToMap(m_ConstMap, name, constNum);
}
else
{
throw new Exception($"ERROR: Enumeration Item Definition Incorrect - <name>: {name} <value>: {constStr}");
}
}
}
private void ProcessStructureNode(XPathNavigator node)
{
string name;
if (node.MoveToChild("name", ""))
{
name = node.Value.Trim();
node.MoveToParent();
}
else
{
throw new Exception("ERROR: Stucture/Message Definition Incorrect. No <name> tag present.");
}
int maxSize = 0;
int padCount = 0;
uint bitCount = 0; // Used to see how many bits have been processed.
int lastItemSize = 0;
var nodeSet = node.Select("item|struct_item|msg_item");
while (nodeSet.MoveNext())
{
GetItemSize(nodeSet.Current, out int padSize, out int itemSize, out int reps, out uint bits);
if ((lastItemSize != itemSize) || ((bitCount + bits) > (uint)(itemSize * 8)))
{
bitCount = 0; // Size changed or bit rollover
}
if (bitCount == 0)
{
padCount += AddPadding(node, nodeSet.Current, padSize, padCount);
// Set maxSize
if (padSize > maxSize)
{
maxSize = padSize;
}
// Keep up with the pad count
padCount += (itemSize * reps);
}
lastItemSize = itemSize;
bitCount += bits;
}
if (maxSize != 0)
{
// Add final padding
padCount += AddPadding(node, null, maxSize, padCount);
}
AddItemToMap(m_PadMap, name, maxSize);
AddItemToMap(m_SizeMap, name, padCount);
}
private void GetItemSize(XPathNavigator node, out int padSize, out int itemSize, out int reps, out uint bits)
{
string name = "";
if ((node.MoveToChild("name", "")) ||
(node.MoveToChild("item_name", "")))
{
name = node.Value.Trim();
node.MoveToParent();
}
itemSize = -1;
padSize = -1;
reps = 1;
bits = 0;
var nodeSet = node.Select("type");
while (nodeSet.MoveNext())
{
GetSizeFromType(nodeSet.Current.Value.Trim(), out padSize, out itemSize);
}
nodeSet = node.Select("bits");
if (nodeSet.MoveNext())
{
bits = (uint)GetConstFromString(nodeSet.Current.Value.Trim());
}
nodeSet = node.Select("arrayLength|imageWidth|imageHeight");
while (nodeSet.MoveNext())
{
try
{
reps *= (int)GetConstFromString(nodeSet.Current.Value.Trim());
}
catch (Exception e)
{
throw new Exception
(e.Message + " item name = \"" + name + "\", tag = <" +
nodeSet.Current.Name + ">.");
}
}
if ((itemSize == -1) || (padSize == -1))
{
throw new Exception
("ERROR: Item named " + name + "does not contain a <type> tag.");
}
if (bits == 0)
bits = (uint)padSize * 8;
}
private double GetConstFromString(string constStr)
{
if ((constStr.Length > 0) && (constStr[0] == '\''))
{
byte charData = (byte)constStr[1];
constStr = charData.ToString();
}
if (Parse.Try(constStr, out double data) == false)
{
if (Parse.Try(constStr, out int iData) == false)
{
try
{
data = m_ConstMap[constStr];
}
catch
{
throw new Exception("ERROR: ConstantValue - \"" + constStr + "\" does not resolve to a number.");
}
}
else
{
data = (double)iData;
}
}
return data;
}
private void AddItemToMap(Dictionary<string, int> map, string name, int value)
{
if (map.ContainsKey(name))
{
throw new Exception("ERROR: Element " + name + " is defined multiple times.");
}
else
{
map.Add(name, value);
}
}
private void AddItemToMap(Dictionary<string, double> map, string name, double value)
{
if (map.ContainsKey(name))
{
throw new Exception("ERROR: Element " + name + " is defined multiple times.");
}
else
{
map.Add(name, value);
}
}
private void GetSizeFromType(string type, out int typePad, out int typeSize)
{
// Remove all whitespace
type = type.Replace(" ", "");
if ((type == "char") || (type == "unsignedchar"))
{
typePad = 1;
typeSize = 1;
}
else if ((type == "short") || (type == "unsignedshort"))
{
typePad = 2;
typeSize = 2;
}
else if ((type == "unsigned") || (type == "unsignedint") ||
(type == "int") || (type == "float") ||
(type == "boolean") || (type == "address"))
{
typePad = 4;
typeSize = 4;
}
else if ((type == "double") || (type == "longlong") ||
(type == "unsignedlonglong"))
{
typePad = 8;
typeSize = 8;
}
else // The type is complex and has already been defined
{
try
{
typePad = m_PadMap[type];
typeSize = m_SizeMap[type];
}
catch
{
throw new Exception("ERROR: <type> - " + type + " used without being defined.");
}
}
}
private int AddPadding(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padSize, int padCount)
{
int padAdd = 0;
int padTo = padSize;
if (m_Packing < padSize)
{
padTo = m_Packing;
}
if ((padTo != 0) && (padCount % padTo != 0))
{
padAdd = padTo - (padCount % padTo);
InsertPaddingNode(ParentElement, CurrentElement, padAdd);
}
return padAdd;
}
private void InsertPaddingNode(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padAdd)
{
string pad = "<item>" +
"<name>" + Message.PADDING_ITEM_NAME + "</name>" +
"<type>char</type>" +
"<arrayLength>" + padAdd + "</arrayLength>" +
"<instruType>S</instruType>" +
"</item>";
if (CurrentElement != null)
{
CurrentElement.InsertBefore(pad);
}
else // End padding
{
ParentElement.AppendChild(pad);
}
}
}
}