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

2235 lines
90 KiB
C#

// **********************************************************************************************************
// Message.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)
// **********************************************************************************************************
// Ignore Spelling: Instru
using Raytheon.Instruments.coeCSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.XPath;
namespace Raytheon.Instruments.MessagingUtilities
{
public class Message : IEnumerable, ICloneable
{
public const string PADDING_ITEM_NAME = "****************";
public string Name { get { return _name; } set { _name = value; } }
public string InstruLabel => _instruLabel;
private string _name;
private string _label;
public string Label { get => _label; }
private string _hexLabel;
private string _instruLabel;
private string _adapationStoreLabel;
private coeDataInterchange.FormatPacketType _packet = null;
//private Logger Log;
private NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
private XPathNavigator Messages;
public MessageData[] MessageDataArray;
private int _packing = 8;
private uint _count = 0;
private readonly int _extraFieldCount = 3;
private ulong _sendSecond = 0;
private uint _sendFracOfSec = 0;
private string _mDomain = "0";
private MessageXmlDocument m_Icd = null;
//Message Maps to support faster creation.
//We only want to parse the XML once.
private Dictionary<string, Message> m_MsgNameMap = null;
private Dictionary<string, Message> m_MsgLabelMap = null;
// Used to protect the critical section
private static readonly object m_LockObj = new object();
// Default values
public static string IntegerDefault = "0";
public static string FloatDefault = "0.00";
public static string HexXDefault = "0x0";
public static string HexBDefault = "0x0";
public static string BinaryDefault = "0";
public static string BitFieldDefault = "0";
public static string StringDefault = "";
public static string SubimageDefault = "c:\\image.bmp";
// Default Logger object
//public static Logger m_DefaultLog = new Logger();
private Message() { }
private Message(MessageXmlDocument MessageDatabase)
{
m_Icd = MessageDatabase;
// Create the static maps if necessary
if ((m_MsgNameMap == null) || (m_MsgLabelMap == null))
{
m_MsgNameMap = new Dictionary<string, Message>();
m_MsgLabelMap = new Dictionary<string, Message>();
}
}
public Message(string messageName, MessageXmlDocument messageDatabase)
: this(messageName, messageDatabase, null) { }
public Message(string messageName, MessageXmlDocument messageDatabase, NLog.Logger logger)
{
m_Icd = messageDatabase;
// Create the static maps if necessary
if ((m_MsgNameMap == null) || (m_MsgLabelMap == null))
{
m_MsgNameMap = new Dictionary<string, Message>();
m_MsgLabelMap = new Dictionary<string, Message>();
}
if (m_MsgNameMap.ContainsKey(messageName) == true)
{
RetrieveClone((Message)m_MsgNameMap[messageName]);
}
else
{
string messageSpecificationString = messageDatabase.GetMessage(messageName);
if (messageSpecificationString == null)
{
throw new Exception(messageName + " Message not found in the ICD.");
}
else
{
Messages = messageDatabase.CreateNavigator();
_name = messageName;
if(logger != null)
_logger = logger;
ParseMessageString(messageSpecificationString);
_logger.Log(NLog.LogLevel.Info, "Loading Message Data...");
Message clone = (Message)Clone();
if (m_MsgNameMap.ContainsKey(_name) == false)
{
m_MsgNameMap.Add(_name, clone);
}
else
{
throw new Exception("Message <" + _name + "> is defined multiple times in the ICD.");
}
if (_label != null)
{
if (m_MsgLabelMap.ContainsKey(_label) == false)
{
m_MsgLabelMap.Add(_label, clone);
}
else
{
m_MsgNameMap.Remove(_name);
throw new Exception("Message <" + _name + "> and Message <" + m_MsgLabelMap[_label]._instruLabel +
"> are both defined with Label " + _label + " in the ICD.");
}
}
}
}
}
/// <summary>
/// This constructor populates the Message based on the Message Label
/// instead of the Message Name
/// </summary>
/// <param name="messageDatabase"></param>
/// <param name="messageLabel"></param>
public Message(MessageXmlDocument messageDatabase, string messageLabel)
: this(messageDatabase, null, messageLabel) { }
public Message(MessageXmlDocument messageDatabase, NLog.Logger logger, string messageLabel)
{
m_Icd = messageDatabase;
// Create the static maps if necessary
if ((m_MsgNameMap == null) || (m_MsgLabelMap == null))
{
m_MsgNameMap = new Dictionary<string, Message>();
m_MsgLabelMap = new Dictionary<string, Message>();
}
if (m_MsgLabelMap.ContainsKey(messageLabel) == true)
{
RetrieveClone((Message)m_MsgLabelMap[messageLabel]);
}
else
{
var messageSpecificationString = messageDatabase.GetMessageFromLabel(messageLabel);
if (messageSpecificationString == null)
{
throw new Exception("Message with label " + messageLabel + " not found in ICD");
}
else
{
Messages = messageDatabase.CreateNavigator();
if (logger != null)
_logger = logger;
ParseMessageString(messageSpecificationString);
_logger.Log(NLog.LogLevel.Info, "Loading Message Data...");
Message clone = (Message)Clone();
if (m_MsgNameMap.ContainsKey(_name) == false)
{
m_MsgNameMap.Add(_name, clone);
}
if (m_MsgLabelMap.ContainsKey(_label) == false)
{
m_MsgLabelMap.Add(_label, clone);
}
}
}
}
public MessageXmlDocument ICD
{
get { return m_Icd; }
}
public string SendTime
{
get
{
double frac = _sendFracOfSec;
frac /= 0x100000000;
string time = _sendSecond.ToString() + frac.ToString(".00000000000000");
return time;
}
}
public ulong SendSecond
{
get { return _sendSecond; }
set { _sendSecond = value; }
}
public uint SendSecondFraction
{
get { return _sendFracOfSec; }
set { _sendFracOfSec = value; }
}
public string Domain
{
get { return _mDomain; }
set { _mDomain = value; }
}
public static void SetDefaults
(
string integerDefault, string floatDefault,
string hexXDefault, string hexBDefault,
string binaryDefault, string bitFieldDefault,
string stringDefault, string subimageDefault
)
{
IntegerDefault = integerDefault;
FloatDefault = floatDefault;
HexXDefault = hexXDefault;
HexBDefault = hexBDefault;
BinaryDefault = binaryDefault;
BitFieldDefault = bitFieldDefault;
StringDefault = stringDefault;
SubimageDefault = subimageDefault;
}
public void Default()
{
SetEnumerationType(EnumerationType.ALL_NODES);
foreach (MessageData data in this)
{
if ((data.isArray != true) &&
(data.isArrayOfStructures != true) &&
(data.isStructure != true))
{
if ((data.FieldDefaultValue != null) && (data.FieldDefaultValue.Trim() != ""))
{
data.FieldValue = data.FieldDefaultValue;
}
else if ((data.FieldMinValue != null) && (data.FieldMinValue.Trim() != ""))
{
data.FieldValue = data.FieldMinValue;
}
else if ((data.FieldMaxValue != null) && (data.FieldMaxValue.Trim() != ""))
{
data.FieldValue = data.FieldMaxValue;
}
else
{
// Get the defaults from the Injection
// pane of the options window
if (data.FieldInstruType == "I")
data.FieldValue = IntegerDefault;
else if (data.FieldInstruType == "F")
data.FieldValue = FloatDefault;
else if (data.FieldInstruType == "X")
data.FieldValue = HexXDefault;
else if (data.FieldInstruType == "B")
data.FieldValue = HexBDefault;
else if (data.FieldInstruType == "N")
data.FieldValue = BinaryDefault;
else if (data.FieldInstruType == "T")
data.FieldValue = BitFieldDefault;
else if (data.FieldInstruType == "S")
data.FieldValue = StringDefault;
else if (data.FieldInstruType == "P")
data.FieldValue = SubimageDefault;
else
data.FieldValue = "";
}
}
}
}
public coeDataInterchange.FormatPacketType GetInstruPacket()
{
if (_packet == null)
{
int count = 0;
uint length = 1;
char instru_type;
uint size;
uint ctr = 0;
MessageData lastData = null;
coeDataInterchange.FormatType lastInstru = null;
coeDataInterchange.FormatType[] format = new coeDataInterchange.FormatType[GetCountForDataInterchange()];
SetEnumerationType(Message.EnumerationType.FIELDS |
Message.EnumerationType.EXPAND_ARRAYS |
Message.EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
if (ctr++ < _extraFieldCount) // Skip $msg_domain, $msg_label and $msg_send_time
continue;
// When handling bit fields, we do not know if we want to move to the
// next byte (Index) until we examine the next element.
//
// ex. unsigned x:4; unsigned y:8; Both x and y operate on the same
// byte, but we don't know the same byte is being operated on until
// we see the declaration of y.
//
// Therefore, we are going to increase the index by staying one declaration
// ahead of the Index increase. If this declaration is operating on a new
// byte, then add the size of the previous byte. The last declaration
// will then be added on the end to get the appropriate size.
if (lastData != null)
{
if (IncreaseIndex(data))
{
length = 1;
size = GetTypeSize(lastData.FieldType, lastData.isEnum);
instru_type = lastData.FieldInstruType[0];
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
length = (uint)lastData.arrayLength;
}
if (lastData.FieldInstruType == "P")
{
size = lastData.imagePixelSize;
length = lastData.imageHeight * lastData.imageWidth;
instru_type = 'X';
}
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
length,
size,
instru_type);
if (lastData.bitMask != 0)
{
if (lastInstru != null)
{
format[count].m_Repetition = 0;
format[count].m_FormatLength = GetNumBitsInMask(lastData.bitMask);
format[count].m_Format = 't';
lastInstru.m_Repetition++;
lastInstru = null;
}
else
{
format[count].m_Repetition = 1;
format[count].m_Format = 'T';
format[count + 1] =
new coeDataInterchange.FormatType(lastData.FieldName,
0,
GetNumBitsInMask(lastData.bitMask),
't');
count++;
}
}
count++;
}
else
{
if (lastInstru == null)
{
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
0,
GetTypeSize(lastData.FieldType, lastData.isEnum),
'T');
lastInstru = format[count];
count++;
}
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
0,
GetNumBitsInMask(lastData.bitMask),
't');
lastInstru.m_Repetition++;
count++;
}
}
lastData = data;
}
if (lastData != null) // The message has 1 or more elements
{
// Add the last element to the size
length = 1;
size = GetTypeSize(lastData.FieldType, lastData.isEnum);
instru_type = lastData.FieldInstruType[0];
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
length = (uint)lastData.arrayLength;
}
if (lastData.FieldInstruType == "P")
{
size = lastData.imagePixelSize;
length = lastData.imageHeight * lastData.imageWidth;
instru_type = 'X';
}
if (lastData.bitMask == 0)
{
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
length,
size,
instru_type);
}
else // (lastData.bitMask != 0)
{
if (lastInstru == null)
{
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
0,
size,
'T');
lastInstru = format[count];
count++;
}
format[count] =
new coeDataInterchange.FormatType(lastData.FieldName,
0,
GetNumBitsInMask(lastData.bitMask),
't');
lastInstru.m_Repetition++;
}
_packet = new coeDataInterchange.FormatPacketType(_name, GetMessageSize(), (uint)format.Length, format);
}
else // There are no items in the message
{
_packet = new coeDataInterchange.FormatPacketType(_name, GetMessageSize(), 0, new coeDataInterchange.FormatType[0]);
}
}
// FOR DEBUG PURPOSES
#if false
Debug.WriteLine("");
Debug.WriteLine("-------------------------------------------------------------------------");
Debug.WriteLine("Message Name = " + Packet.m_Name);
Debug.WriteLine("");
foreach (DataInterchange.FormatType f in Packet.m_Format)
Debug.WriteLine("\"" + f.m_FieldName +
"\", " + f.m_Repetition +
", " + f.m_FormatLength +
", \'" + f.m_Format +
"\'");
Debug.WriteLine("-------------------------------------------------------------------------");
#endif
return _packet;
}
private uint GetNumBitsInMask(ulong mask)
{
uint count = 0;
ulong bit = 1;
for (int i = 0; i < sizeof(ulong) * 8; i++)
{
if ((bit & mask) != 0)
{
count++;
}
bit <<= 1;
}
return count;
}
private void ParseMessageString(string messageSpecificationString)
{
lock (m_LockObj)
{
var messageSpecification = new XmlDocument();
int index = 0;
messageSpecification.LoadXml(messageSpecificationString);
var item = messageSpecification.CreateNavigator();
//Get the type of packing
var nodeset = item.Select("/packing");
if (nodeset.MoveNext() == true)
{
if (!Parse.Try(nodeset.Current.Value.Trim(), out _packing))
{
switch (nodeset.Current.Value.Trim())
{
case "char":
_packing = 1;
break;
case "short":
_packing = 2;
break;
case "long":
_packing = 4;
break;
case "double":
default:
_packing = 8;
break;
}
}
}
// Three extra fields will be added to the top of every
// Message in CMIT, $msg_domain, $msg_label, $msg_send_time
nodeset = item.Select("/message/item|/message/msg_item");
MessageDataArray = new MessageData[nodeset.Count + _extraFieldCount];
//MessageData.m_BitCounter = 0; // Reset the bit counter for each new message
item.MoveToChild("message", "");
if (item.MoveToChild("name", ""))
{
_name = item.Value.Trim();
item.MoveToParent();
}
if (item.MoveToChild("label", ""))
{
// Convert the label from whatever format it is in to decimal.
uint msgLabel = 0;
_label = NormalizeValue(item.Value.Trim());
if (Parse.Try(_label, out int temp) == false)
{
throw new Exception("Message Label for " + _name + " could not be evaluated to an integer");
}
if (temp < 0)
{
byte[] array = BitConverter.GetBytes(temp);
msgLabel = BitConverter.ToUInt32(array, 0);
}
else
msgLabel = Convert.ToUInt32(temp);
_label = msgLabel.ToString();
_hexLabel = msgLabel.ToString("X");
item.MoveToParent();
}
if (item.MoveToChild("instruLabel", ""))
{
_instruLabel = item.Value.Trim();
item.MoveToParent();
}
if (item.MoveToChild("adaptationSaveLabel", ""))
{
_adapationStoreLabel = item.Value.Trim();
item.MoveToParent();
}
MessageDataArray[index] = new MessageData("$msg_domain",
"unsigned int", Domain, Domain, m_Icd);
MessageDataArray[index++].FieldInstruType = "X";
MessageDataArray[index] = new MessageData("$msg_label",
"unsigned int", _label, _label, m_Icd);
MessageDataArray[index++].FieldInstruType = "X";
MessageDataArray[index] = new MessageData("$msg_send_time",
"double", null, null, m_Icd);
MessageDataArray[index++].FieldInstruType = "F";
while (nodeset.MoveNext())
{
AddToMessageData(nodeset.Current.Clone(), "", MessageDataArray, index++, 0);
}
}
}
private void ParseMessageString(string messageSpecificationString,
string messageSelectionString,
string namePrefix,
MessageData parentField)
{
var messageSpecification = new XmlDocument();
int index = 0;
messageSpecification.LoadXml(messageSpecificationString);
XPathNavigator item = messageSpecification.CreateNavigator();
XPathNodeIterator nodeset = item.Select(messageSelectionString);
parentField.SetStructureSize(nodeset.Count);
while (nodeset.MoveNext())
{
AddToMessageData(nodeset.Current.Clone(), namePrefix, parentField.MessageArray, index++, parentField.depth + 1);
}
}
private void AddToMessageData(XPathNavigator item,
string namePrefix,
MessageData[] dataArray,
int arrayIndex,
int depth)
{
string fieldName = null;
string fieldType = null;
string fieldDefaultValue = null;
XPathNavigator structureDefinition;
bool isEnum = false;
int arrayLength;
LinkedList<int> length = new LinkedList<int>();
//
// Extract the name, type, default value, and array length values
// for the field
//
if (item.MoveToChild("name", ""))
{
fieldName = item.Value.Trim();
item.MoveToParent();
}
if (item.MoveToChild("item_name", ""))
{
fieldName = item.Value.Trim();
item.MoveToParent();
}
if (item.MoveToChild("type", ""))
{
fieldType = NormalizeType(item.Value, ref isEnum);
item.MoveToParent();
}
if (item.MoveToChild("default", ""))
{
fieldDefaultValue = NormalizeValue(item.Value);
item.MoveToParent();
}
else if (item.MoveToChild("minRange", ""))
{
fieldDefaultValue = NormalizeValue(item.Value);
item.MoveToParent();
}
//
// Check to see if this field is a structure by locating the structure
// definition.
//
structureDefinition = Messages.SelectSingleNode("/interface/structure[name='" + fieldType + "']|/interface/message[name='" + fieldType + "']");
//
// Create the MessageData object for this field and put it in the array.
// Set max and min range values if they exist.
//
dataArray[arrayIndex] = new MessageData(namePrefix + fieldName,
fieldType, null, fieldDefaultValue, m_Icd);
//If Type is an enum sent the isEnum field to true
if (isEnum)
{
dataArray[arrayIndex].isEnum = true;
}
XPathNodeIterator nodeSet = item.Select("arrayLength");
while (nodeSet.MoveNext())
{
arrayLength = 0;
Parse.Try(NormalizeValue(nodeSet.Current.Value), out arrayLength);
if (arrayLength != 0)
{
length.AddFirst(arrayLength);
}
}
if (item.MoveToChild("imageWidth", ""))
{
Parse.Try(NormalizeValue(item.Value), out uint width);
if (width != 0)
{
dataArray[arrayIndex].imageWidth = width;
}
item.MoveToParent();
}
if (item.MoveToChild("imageHeight", ""))
{
Parse.Try(NormalizeValue(item.Value), out uint height);
if (height != 0)
{
dataArray[arrayIndex].imageHeight = height;
}
item.MoveToParent();
}
if (item.MoveToChild("imagePixelSize", ""))
{
Parse.Try(NormalizeValue(item.Value), out uint pixelSize);
if (pixelSize != 0)
{
dataArray[arrayIndex].imagePixelSize = pixelSize;
}
item.MoveToParent();
}
if (item.MoveToChild("maxRange", ""))
{
dataArray[arrayIndex].SetMaxValue(item.Value.Trim());
item.MoveToParent();
}
if (item.MoveToChild("minRange", ""))
{
dataArray[arrayIndex].SetMinValue(item.Value.Trim());
item.MoveToParent();
}
if (item.MoveToChild("bits", ""))
{
Parse.Try(NormalizeValue(item.Value), out int bits);
dataArray[arrayIndex].SetBitValue(bits);
item.MoveToParent();
}
else
{
dataArray[arrayIndex].SetBitValue(0);
}
if (item.MoveToChild("instruType", ""))
{
dataArray[arrayIndex].SetInstruType(item.Value.Trim());
if ((dataArray[arrayIndex].FieldInstruType == "S") && (dataArray[arrayIndex].FieldType == "unsigned char"))
{
// In CMIT, all strings are of type char. If the user defines a string of unsigned chars, we
// can still handle them if type is changed to char.
dataArray[arrayIndex].FieldType = "char";
}
else if ((dataArray[arrayIndex].FieldInstruType == "S") && (dataArray[arrayIndex].FieldType != "char"))
{
// If the type is not char, the CMIT will not handle strings. We will change the instruType
// to X, so these fields are handled properly.
dataArray[arrayIndex].FieldInstruType = "X";
}
item.MoveToParent();
}
// Initialize the image buffer if this is a subimage item
if ((dataArray[arrayIndex].imageWidth != 0) && (dataArray[arrayIndex].imageHeight != 0))
{
dataArray[arrayIndex].imageBufferSize =
dataArray[arrayIndex].imageWidth *
dataArray[arrayIndex].imageHeight *
GetTypeSize(dataArray[arrayIndex].FieldType, dataArray[arrayIndex].isEnum);
dataArray[arrayIndex].imageBuffer = new byte[dataArray[arrayIndex].imageBufferSize];
dataArray[arrayIndex].SetInstruType("P");
dataArray[arrayIndex].SetPixelSize();
}
else if (dataArray[arrayIndex].FieldInstruType == "P")
{
// If no imageWidth and imageHeight are defined, then
// this item can not have an instruType of "P"
dataArray[arrayIndex].FieldInstruType = "X";
}
dataArray[arrayIndex].depth = depth;
//
// If the field is a singleton, we're done. Other combinations
// could be an array, a structure, or an array of structures.
//
if (length.Count > 0)
{
ParseArray(structureDefinition,
dataArray[arrayIndex],
length);
}
else if (structureDefinition != null)
{
ParseMessageString(structureDefinition.OuterXml, "/structure/item|/message/item|/structure/struct_item|/message/msg_item", dataArray[arrayIndex].FieldName + ".",
dataArray[arrayIndex]);
}
}
private void ParseArray
(
XPathNavigator StructureDefinition,
MessageData DataArray,
LinkedList<int> ArrayLength
)
{
int length = ArrayLength.Last.Value;
ArrayLength.RemoveLast();
if (length > 0)
{
// If the array type is an enumeration type, need to make it an integer
XPathNavigator navigator = m_Icd.CreateNavigator();
XPathNavigator position;
try
{
position = navigator.SelectSingleNode("/interface/enum[name='" + DataArray.FieldType + "']");
if (position != null)
{
DataArray.FieldType = "unsigned int";
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
DataArray.SetArraySize(length);
if (ArrayLength.Count != 0)
{
for (int index = 0; index < length; index++)
{
ParseArray(StructureDefinition,
DataArray.MessageArray[index],
ArrayLength);
}
}
else
{
if (StructureDefinition != null)
{
ParseMessageString(StructureDefinition.OuterXml, "/structure/item|/message/item|/structure/struct_item|/message/msg_item",
DataArray.MessageArray[0].FieldName + ".",
DataArray.MessageArray[0]);
for (int index = 1; index < length; index++)
{
DataArray.MessageArray[index].isStructure = true;
DataArray.MessageArray[index].MessageArray = CloneMessageData(DataArray.MessageArray[0].MessageArray);
CorrectClonedArrayIndex(DataArray.MessageArray[index].MessageArray,
DataArray.MessageArray[0].FieldName.Trim(),
DataArray.MessageArray[index].FieldName.Trim());
}
DataArray.isArrayOfStructures = true;
}
}
}
ArrayLength.AddFirst(length);
}
private void CorrectClonedArrayIndex(MessageData[] array,
string old_str,
string new_str)
{
foreach (MessageData data in array)
{
// We want to replace the index in the name of the structure. The "begin" substring
// ensures we only replace the first instance of the string
string begin = data.FieldName.Substring(0, new_str.Length);
begin = begin.Replace(old_str, new_str);
data.FieldName = begin + data.FieldName.Substring(new_str.Length);
if (data.MessageArray != null)
{
CorrectClonedArrayIndex(data.MessageArray, old_str, new_str);
}
}
}
internal string NormalizeValue(string value)
{
XPathNavigator messagesItem;
bool iterating;
value = value.Trim();
do
{
iterating = false;
if ((value.Length > 0) && (value[0] != '\"') && (value[0] != '\''))
{
messagesItem = Messages.SelectSingleNode("/interface/constant[name='" + value + "']|/interface/enum/item[name='" + value + "']");
if (messagesItem == null)
{
messagesItem = Messages.SelectSingleNode("/interface/constant[name='" + value + "']|/interface/enum/enum_item[name='" + value + "']");
}
if (messagesItem != null)
{
messagesItem.MoveToChild("value", "");
value = messagesItem.InnerXml;
iterating = true;
}
}
} while (iterating);
return value;
}
private string NormalizeType(string value, ref bool isEnum)
{
XPathNavigator MessagesItem;
bool iterating;
do
{
iterating = false;
MessagesItem = Messages.SelectSingleNode("/interface/typedef[name='" + NormalizeValue(value) + "']");
if (MessagesItem != null)
{
MessagesItem.MoveToChild("value", "");
value = MessagesItem.InnerXml;
iterating = true;
}
//Check if Value is an enumeration
MessagesItem = Messages.SelectSingleNode("/interface/enum[name='" + NormalizeValue(value) + "']");
if (MessagesItem != null)
{
isEnum = true;
}
} while (iterating);
return value;
}
public void Reset()
{
SetEnumerationType(EnumerationType.EXPANDED_FIELDS);
foreach (MessageData data in this)
{
data.FieldValue = null;
data.usesRegister = false;
}
}
public MessageData Set(string field, string value)
{
MessageData LocatedDataItem = null;
SetEnumerationType(EnumerationType.ALL_NODES);
foreach (MessageData data in this)
{
if (string.Compare(field, data.FieldName) == 0)
{
if (data.isArrayOfStructures)
{
; // Do nothing in this case
}
if (data.isArray || data.isStructure)
{
data.FieldValue = value;
if (value.StartsWith("$"))
{
data.usesRegister = true;
}
foreach (MessageData ArrayData in data.MessageArray)
{
if (!ArrayData.isArray && !ArrayData.isArrayOfStructures && !ArrayData.isStructure)
{
ArrayData.FieldArrayValue = value;
if (value.StartsWith("$"))
{
ArrayData.usesRegister = true;
}
}
}
}
else
{
data.FieldValue = value;
if (value.StartsWith("$"))
{
data.usesRegister = true;
}
LocatedDataItem = data;
}
break;
}
}
return LocatedDataItem;
}
public MessageData GetItemFromMsg(string name)
{
// Add each message item to the tree
SetEnumerationType(EnumerationType.ALL_NODES);
foreach (MessageData Data in this)
{
// Find the Item that has been selected in current Message
if (Data.FieldName == name)
{
return Data;
}
}
return null;
}
/// <summary>
/// logs message with all the fields
/// </summary>
/// <param name="enumerationType"></param>
/// <param name="direction"></param>
public void SendToLog(EnumerationType enumerationType = EnumerationType.EXPANDED_FIELDS, MessageDirection direction = MessageDirection.File)
{
string directionSign = direction == MessageDirection.File ? "_" : direction == MessageDirection.In ? "<<<" : ">>>";
_logger.Log(NLog.LogLevel.Info, $"{directionSign} Message Name: {_name}; Label: {_label} (0x{_hexLabel})");
SetEnumerationType(enumerationType);
foreach (MessageData data in this)
{
string displayFieldValue;
if(data.FieldName == "$msg_label")
{
displayFieldValue = $"{data.FieldValue} (0x{_hexLabel})";
}
else
{
displayFieldValue = data.FieldValue ?? $"{data.FieldDefaultValue} (D)";
}
var spacer = new string(Enumerable.Repeat(' ', data.depth * 4).ToArray());
_logger.Log(NLog.LogLevel.Info, $"{spacer}{data.FieldName} ({data.FieldType}) = {displayFieldValue}");
}
}
// helps with visual identification in the log of incoming vs outgoing
public enum MessageDirection
{
File,
In,
Out
}
// This class supplies an enumerator that is capable of iterating over the fields in the
// message with a variety of options. The SetEnumerationType method sets how the enumerator
// will iterate over the fields. The enumeration values can be added to derive various
// combinations of iterations. The values are as follows:
// FIELDS Only non array and structure fields
// ARRAY_NODES The message_data nodes that have isArray set
// STRUCTURE_NODES The message_data nodes that have isStructure set
// ARRAY_PRIMITIVE_NODES Nodes with isArray set that are not arrays of structures
// EXPAND_ARRAYS The array fields linked onto an array node
// EXPAND_STRUCTURES The structure fields linked onto a structure node
// TOP_NODES Top level Fields and array/structure nodes
// EXPANDED_FIELDS All fields, structures and arrays expanded
// ALL_NODES All nodes (the sum of all the above)
//
public enum EnumerationType
{
FIELDS = 0x01,
ARRAY_NODES = 0x02,
STRUCTURE_NODES = 0x04,
ARRAY_PRIMITIVE_NODES = 0x08,
EXPAND_ARRAYS = 0x10,
EXPAND_STRUCTURES = 0x20,
TOP_NODES = 0x07,
EXPANDED_FIELDS = 0x31,
ALL_NODES = 0xFF
};
private EnumerationType EnumType = EnumerationType.TOP_NODES;
public void SetEnumerationType(EnumerationType type)
{
EnumType = type;
}
public IEnumerator GetEnumerator()
{
return new MessageEnumerator(MessageDataArray, EnumType);
}
private class MessageEnumerator : IEnumerator
{
private int CurrentIndex;
private bool doExpandArray;
private readonly EnumerationType enumType = EnumerationType.TOP_NODES;
private readonly MessageData[] RootMessageArray;
private MessageData[] CurrentMessageArray;
private readonly Stack<int> indexStack = new Stack<int>();
private readonly Stack<MessageData[]> messageStack = new Stack<MessageData[]>();
private MessageEnumerator()
{
Reset();
}
public MessageEnumerator(MessageData[] Root, EnumerationType Type)
{
RootMessageArray = Root;
enumType = Type;
Reset();
}
public void Reset()
{
CurrentIndex = -1;
CurrentMessageArray = RootMessageArray;
doExpandArray = false;
indexStack.Clear();
messageStack.Clear();
}
public bool MoveNext()
{
bool Scanning = true;
bool Status = true;
while (Scanning)
{
CurrentIndex++;
//
// If need to start expanding an array (array or structure), do so
//
if (doExpandArray)
{
doExpandArray = false;
indexStack.Push(CurrentIndex);
messageStack.Push(CurrentMessageArray);
CurrentMessageArray = CurrentMessageArray[CurrentIndex - 1].MessageArray;
CurrentIndex = -1;
}
//
// Else see if this node needs to be returned
//
else
{
//
// If at the end of an array, pop the stacks
//
while (CurrentIndex >= CurrentMessageArray.Length)
{
if (indexStack.Count == 0)
{
return false; // End of iteration
}
else
{
CurrentIndex = indexStack.Pop();
CurrentMessageArray = messageStack.Pop();
}
}
if (CurrentMessageArray[CurrentIndex].isStructure)
{
if ((enumType & EnumerationType.STRUCTURE_NODES) != 0)
{
Scanning = false;
}
if ((enumType & EnumerationType.EXPAND_STRUCTURES) != 0)
{
doExpandArray = true;
}
}
else if (CurrentMessageArray[CurrentIndex].isArray)
{
if ((enumType & EnumerationType.ARRAY_NODES) != 0)
{
Scanning = false;
}
if ((enumType & EnumerationType.ARRAY_PRIMITIVE_NODES) != 0)
{
if (!CurrentMessageArray[CurrentIndex].isArrayOfStructures)
{
Scanning = false;
}
}
if ((enumType & EnumerationType.EXPAND_ARRAYS) != 0)
{
doExpandArray = true;
}
}
else
{
if ((enumType & EnumerationType.FIELDS) != 0)
{
Scanning = false;
}
}
}
}
return Status;
}
public object Current
{
get
{
return CurrentMessageArray[CurrentIndex];
}
}
}
enum TypeSize
{
INT = 4,
UINT = 4,
CHAR = 1,
SHORT = 2,
USHORT = 2,
UNSIGNED_CHAR = 1,
BOOL = 1,
DOUBLE = 8,
LONGLONG = 8,
FLOAT = 4,
ENUM = 4
}
public void MsgDataToBuffer(ref byte[] DataBuffer, out uint MessageSize)
{
int Index = 0;
uint pictureSize;
int Offset = 0;
MessageData lastData = null;
byte[] ByteArray;
int ctr = 0;
// Data buffer was just passed from a fresh "new byte[]" so all values are guaranteed to
// be 0 at the beginning.
//
// For each element in data element in the message, the string representation of
// the data will be passed to the BitFieldGeneric class with its bit mask to
// be converted into a binary value. The BitFieldGeneric value will then be
// Bitwise ORed to buffer, allowing bit fields to be handled properly. This
// ORed value will then be converted into a byte array and placed in the data
// buffer for transfer.
SetEnumerationType(EnumerationType.FIELDS |
EnumerationType.EXPAND_ARRAYS |
EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
//Handle $msg_domain, $msg_label and $msg_send_time appropriately
if (ctr == 0) // handle $msg_domain
{
Domain = data.FieldValue;
ctr++;
continue;
}
else if (ctr == 1) // handle $msg_label
{
// Do we want the user to be able to change the label on the fly
//Label = data.FieldValue;
ctr++;
continue;
}
else if (ctr == 2) // handle $msg_send_time
{
ctr++;
continue;
}
// Padding may have FieldValue set to null, change it to ""
if (data.FieldValue == null)
data.FieldValue = "";
// This increases the index into the buffer. See GetMessageSize()
// for a detailed explanation of the algorithm
if (lastData != null)
{
if (IncreaseIndex(data))
{
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
Index += (int)TypeSize.CHAR * lastData.arrayLength;
}
else if (lastData.FieldInstruType == "P")
{
pictureSize = GetTypeSize(lastData.FieldType, lastData.isEnum);
pictureSize *= lastData.imageWidth * lastData.imageHeight;
Index += (int)pictureSize;
}
else
{
Index += (int)GetTypeSize(lastData.FieldType, lastData.isEnum);
}
}
}
lastData = data;
// Process each message element based on its type
if ((data.FieldInstruType == "P") && (data.imageBuffer != null))
{
data.imageBuffer.CopyTo(DataBuffer, Index);
}
else
{
switch (data.FieldType)
{
case "int":
BitFieldGeneric<int> iParsedValue = new BitFieldGeneric<int>(data.FieldValue, data.bitMask);
iParsedValue.BitOR(BitConverter.ToInt32(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(iParsedValue.ToInt());
ByteArray.CopyTo(DataBuffer, Index);
break;
case "char":
decimal ParsedValue;
if (data.FieldInstruType == "S")
{
// If this is a string, copy the entire string
for (int i = 0; i < data.arrayLength; i++)
{
if (i < data.FieldValue.Length)
{
DataBuffer[Index + i] = (byte)data.FieldValue[i];
}
else
{
DataBuffer[Index + i] = 0;
}
}
}
else // This is not a string
{
if (Parse.Try(data.FieldValue, out ParsedValue))
{
BitFieldGeneric<sbyte> sbParsedValue = new BitFieldGeneric<sbyte>
(data.FieldValue, data.bitMask);
sbParsedValue.BitOR((sbyte)DataBuffer[Index]);
DataBuffer[Index] = (byte)sbParsedValue.ToSByte();
}
else if ((data.FieldValue != null) && (data.FieldValue.Length > 0))
{
DataBuffer[Index] = (byte)data.FieldValue[0];
}
else
{
DataBuffer[Index] = 0;
}
}
break;
case "short":
BitFieldGeneric<short> sParsedValue = new BitFieldGeneric<short>(data.FieldValue, data.bitMask);
sParsedValue.BitOR(BitConverter.ToInt16(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(sParsedValue.ToShort());
ByteArray.CopyTo(DataBuffer, Index);
break;
case "unsigned char":
case "unsignedchar":
if (Parse.Try(data.FieldValue, out ParsedValue))
{
BitFieldGeneric<byte> sbParsedValue = new BitFieldGeneric<byte>(data.FieldValue, data.bitMask);
sbParsedValue.BitOR(DataBuffer[Index]);
DataBuffer[Index] = (byte)sbParsedValue.ToSByte();
}
else if ((data.FieldValue != null) && (data.FieldValue.Length > 0))
{
DataBuffer[Index] = (byte)data.FieldValue[0];
}
else
{
DataBuffer[Index] = 0;
}
break;
case "unsigned short":
case "unsignedshort":
BitFieldGeneric<ushort> usParsedValue = new BitFieldGeneric<ushort>(data.FieldValue, data.bitMask);
usParsedValue.BitOR(BitConverter.ToUInt16(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(usParsedValue.ToUShort());
ByteArray.CopyTo(DataBuffer, Index);
break;
case "unsigned int":
case "unsignedint":
case "unsigned":
case "boolean":
case "address":
BitFieldGeneric<uint> uiParsedValue = new BitFieldGeneric<uint>(data.FieldValue, data.bitMask);
uiParsedValue.BitOR(BitConverter.ToUInt32(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(uiParsedValue.ToUInt());
ByteArray.CopyTo(DataBuffer, Index + Offset);
break;
case "float":
float FlParsedValue;
if (!Parse.Try(string.IsNullOrEmpty(data.FieldValue) ? "0" : data.FieldValue, out FlParsedValue))
{
throw new Exception($"Unable to parse value = {data.FieldValue} ");
}
ByteArray = BitConverter.GetBytes(FlParsedValue);
ByteArray.CopyTo(DataBuffer, Index);
break;
case "double":
double DbParsedValue;
if (!Parse.Try(string.IsNullOrEmpty(data.FieldValue) ? "0" : data.FieldValue, out DbParsedValue))
{
throw new Exception($"Unable to parse value = {data.FieldValue} ");
}
ByteArray = BitConverter.GetBytes(DbParsedValue);
ByteArray.CopyTo(DataBuffer, Index);
break;
case "long long":
case "longlong":
BitFieldGeneric<long> llParsedValue = new BitFieldGeneric<long>(data.FieldValue, data.bitMask);
llParsedValue.BitOR(BitConverter.ToInt64(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(llParsedValue.ToLong());
ByteArray.CopyTo(DataBuffer, Index);
break;
case "unsigned long long":
case "unsignedlonglong":
BitFieldGeneric<ulong> ullParsedValue = new BitFieldGeneric<ulong>(data.FieldValue, data.bitMask);
ullParsedValue.BitOR(BitConverter.ToUInt64(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(ullParsedValue.ToULong());
ByteArray.CopyTo(DataBuffer, Index);
break;
default:
if (data.isEnum)
{
data.FieldValue = GetEnumerationValue(data.FieldValue, data.FieldType);
BitFieldGeneric<uint> eParsedValue = new BitFieldGeneric<uint>(data.FieldValue, data.bitMask);
eParsedValue.BitOR(BitConverter.ToUInt32(DataBuffer, Index));
ByteArray = BitConverter.GetBytes(eParsedValue.ToUInt());
ByteArray.CopyTo(DataBuffer, Index);
}
break;
}
}
}
if (lastData != null) // Ensure the message has items
{
// Add the last element to the size
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
Index += (int)TypeSize.CHAR * lastData.arrayLength;
}
else if (lastData.FieldInstruType == "P")
{
pictureSize = GetTypeSize(lastData.FieldType, lastData.isEnum);
pictureSize *= lastData.imageWidth * lastData.imageHeight;
Index += (int)pictureSize;
}
else
{
Index += (int)GetTypeSize(lastData.FieldType, lastData.isEnum);
}
}
MessageSize = (uint)Index;
}
private string GetEnumerationValue(string Value, string Type)
{
uint value; // enum value
string returnValue = "";
if (Parse.Try(Value, out value) == false)
{
Dictionary<string, string> enums = m_Icd.GetEnumerations(Type);
foreach (KeyValuePair<string, string> pair in enums)
{
if (Value == pair.Key)
{
returnValue = pair.Value;
break;
}
}
}
else
{
returnValue = value.ToString();
}
if (returnValue == "")
{
throw new Exception("Unable to parse value = " + Value + ". No valid enumeration exists.");
}
return returnValue;
}
public bool BufferToMsgData(byte[] DataBuffer)
{
int index = 0;
MessageData lastData = null;
int ctr = 0;
SetEnumerationType(EnumerationType.FIELDS |
EnumerationType.EXPAND_ARRAYS |
EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
if (ctr == 0) // handle $msg_domain
{
data.FieldValue = Domain;
ctr++;
continue;
}
else if (ctr == 1) // handle $msg_label
{
data.FieldValue = _label;
ctr++;
continue;
}
else if (ctr == 2) // handle $msg_send_time
{
data.FieldValue = SendTime;
ctr++;
continue;
}
// This increases the index into the buffer. See GetMessageSize()
// for a detailed explanation of the algorithm
if (lastData != null)
{
if (IncreaseIndex(data))
{
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
index += (int)TypeSize.CHAR * lastData.arrayLength;
}
else if (lastData.FieldInstruType == "P")
{
uint pictureSize = GetTypeSize(lastData.FieldType, lastData.isEnum);
pictureSize *= lastData.imageWidth * lastData.imageHeight;
index += (int)pictureSize;
}
else
{
index += (int)GetTypeSize(lastData.FieldType, lastData.isEnum);
}
}
}
lastData = data;
// Process each message element based on its type
if ((data.FieldInstruType == "P") && (data.imageBuffer != null))
{
for (int i = 0; i < data.imageBuffer.Length; i++)
{
data.imageBuffer[i] = DataBuffer[index + i];
}
data.FieldValue = "--- Subimage --- ";
}
else
{
switch (data.FieldType)
{
case "int":
BitFieldGeneric<int> IValue = new BitFieldGeneric<int>
(BitConverter.ToInt32(DataBuffer, index), data.bitMask);
IValue.ToSigned();
data.FieldValue = IValue.ToString();
break;
case "char":
if (data.FieldInstruType == "S")
{
// Handle string
data.FieldValue = "";
for (int i = 0; i < data.arrayLength; i++)
{
if (DataBuffer[index + i] == 0) break;
data.FieldValue += (char)DataBuffer[index + i];
}
}
else
{
BitFieldGeneric<sbyte> CValue = new BitFieldGeneric<sbyte>
((sbyte)DataBuffer[index], data.bitMask);
CValue.ToSigned();
data.FieldValue = CValue.ToString();
}
break;
case "short":
BitFieldGeneric<short> SValue = new BitFieldGeneric<short>
(BitConverter.ToInt16(DataBuffer, index), data.bitMask);
SValue.ToSigned();
data.FieldValue = SValue.ToString();
break;
case "unsigned char":
case "unsignedchar":
BitFieldGeneric<byte> SBValue = new BitFieldGeneric<byte>
(DataBuffer[index], data.bitMask);
data.FieldValue = SBValue.ToString(); /*ACB*/
//data.FieldValue = DataBuffer[Index].ToString();
break;
case "unsigned short":
case "unsignedshort":
BitFieldGeneric<ushort> USValue = new BitFieldGeneric<ushort>
(BitConverter.ToUInt16(DataBuffer, index), data.bitMask);
data.FieldValue = USValue.ToString();
break;
case "unsigned int":
case "unsignedint":
case "unsigned":
case "address":
case "boolean":
BitFieldGeneric<uint> UIValue = new BitFieldGeneric<uint>
(BitConverter.ToUInt32(DataBuffer, index), data.bitMask);
data.FieldValue = UIValue.ToString();
break;
case "float":
float FValue = BitConverter.ToSingle(DataBuffer, index);
data.FieldValue = FValue.ToString();
break;
case "double":
double DValue = BitConverter.ToDouble(DataBuffer, index);
data.FieldValue = DValue.ToString();
break;
case "long long":
case "longlong":
BitFieldGeneric<long> LLValue = new BitFieldGeneric<long>
(BitConverter.ToInt64(DataBuffer, index), data.bitMask);
LLValue.ToSigned();
data.FieldValue = LLValue.ToString();
break;
case "unsigned long long":
case "unsignedlonglong":
BitFieldGeneric<ulong> ULLValue = new BitFieldGeneric<ulong>
(BitConverter.ToUInt64(DataBuffer, index), data.bitMask);
data.FieldValue = ULLValue.ToString();
break;
default:
if (data.isEnum)
{
BitFieldGeneric<uint> EnumValue = new BitFieldGeneric<uint>
(BitConverter.ToUInt32(DataBuffer, index), data.bitMask);
data.FieldValue = EnumValue.ToString();
}
break;
}
}
}
return true;
}
internal uint GetCountForDataInterchange()
{
uint ctr = 0;
if (_count == 0)
{
MessageData lastData = null;
SetEnumerationType(Message.EnumerationType.FIELDS |
Message.EnumerationType.EXPAND_ARRAYS |
Message.EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
if (ctr++ < _extraFieldCount) // Skip $msg_domain, $msg_label and $msg_send_time
continue;
// When handling bit fields, we do not know if we want to move to the
// next byte (Index) until we examine the next element.
//
// ex. unsigned x:4; unsigned y:8; Both x and y operate on the same
// byte, but we don't know the same byte is being operated on until
// we see the declaration of y.
//
// Therefore, we are going to increase the index by staying one declaration
// ahead of the Index increase. If this declaration is operating on a new
// byte, then add the size of the previous byte. The last declaration
// will then be added on the end to get the appropriate size.
if (lastData != null)
{
if (IncreaseIndex(data))
{
_count++;
if (lastData.bitMask != 0)
{
_count++;
}
}
else
{
_count++;
}
}
lastData = data;
}
// Add the last element to the size
_count++;
if (lastData.bitMask != 0)
{
_count++;
}
}
return (uint)_count;
}
public uint GetMessageSize()
{
uint Index = 0;
uint pictureSize;
MessageData lastData = null;
uint ctr = 0;
SetEnumerationType(Message.EnumerationType.FIELDS |
Message.EnumerationType.EXPAND_ARRAYS |
Message.EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
if (ctr++ < _extraFieldCount) // Skip $msg_domain, $msg_label and $msg_send_time
continue;
// When handling bit fields, we do not know if we want to move to the
// next byte (Index) until we examine the next element.
//
// ex. unsigned x:4; unsigned y:8; Both x and y operate on the same
// byte, but we don't know the same byte is being operated on until
// we see the declaration of y.
//
// Therefore, we are going to increase the index by staying one declaration
// ahead of the Index increase. If this declaration is operating on a new
// byte, then add the size of the previous byte. The last declaration
// will then be added on the end to get the appropriate size.
if (lastData != null)
{
if (IncreaseIndex(data))
{
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
Index += (uint)TypeSize.CHAR * (uint)lastData.arrayLength;
}
else if (lastData.FieldInstruType == "P")
{
pictureSize = GetTypeSize(lastData.FieldType, lastData.isEnum);
pictureSize *= lastData.imageWidth * lastData.imageHeight;
Index += pictureSize;
}
else
{
Index += GetTypeSize(lastData.FieldType, lastData.isEnum);
}
}
}
lastData = data;
}
if (lastData != null) // Ensure there are items in the message
{
// Add the last element to the size
if ((lastData.FieldType == "char") && (lastData.FieldInstruType == "S"))
{
// Strings are handled individually
Index += (uint)TypeSize.CHAR * (uint)lastData.arrayLength;
}
else if (lastData.FieldInstruType == "P")
{
pictureSize = GetTypeSize(lastData.FieldType, lastData.isEnum);
pictureSize *= lastData.imageWidth * lastData.imageHeight;
Index += pictureSize;
}
else
{
Index += GetTypeSize(lastData.FieldType, lastData.isEnum);
}
}
return (uint)Index;
}
private bool IncreaseIndex(MessageData Data)
{
if ((Data.bitMask == 0) || ((Data.bitMask & 1) == 1))
{
return true;
}
else
{
return false;
}
}
static internal uint GetTypeSize(string Type, bool IsEnum)
{
uint size = 4;
switch (Type)
{
case "char":
case "unsigned char":
case "unsignedchar":
size = (uint)TypeSize.CHAR;
break;
case "short":
case "unsigned short":
case "unsignedshort":
size = (uint)TypeSize.SHORT;
break;
case "int":
case "unsigned int":
case "unsignedint":
case "unsigned":
case "address":
case "boolean":
case "float":
size = (uint)TypeSize.INT;
break;
case "double":
case "long long":
case "longlong":
case "unsigned long long":
case "unsignedlonglong":
size = (uint)TypeSize.LONGLONG;
break;
default:
if (IsEnum == true)
size = (uint)TypeSize.ENUM;
//else
//throw new Exception("Invalid Type");
break;
}
return size;
}
//
// This function validates that the FieldValue is of the correct type and
// is within the Max and Min values. If all the data is valid this
// function returns true. If the data is invalid the function returns
// false and the inValid MessageData elements will have there isValid
// field set to false.
//
public bool ValidateMsgData()
{
bool DataValid = true;
SetEnumerationType(Message.EnumerationType.FIELDS |
Message.EnumerationType.EXPAND_ARRAYS |
Message.EnumerationType.EXPAND_STRUCTURES);
foreach (MessageData data in this)
{
data.isValid = true;
switch (data.FieldType)
{
case "int":
int IntParsedValue;
int IMaxValue, IMinValue;
if (!Parse.Try(data.FieldValue, out IntParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out IMinValue) &&
Parse.Try(data.FieldMaxValue, out IMaxValue))
{
if (IntParsedValue < IMinValue || IntParsedValue > IMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "char":
char ChParsedValue;
byte ByteParsedValue;
//If the value can be parsed as a byte assume it is a byte, if not try as char
if (Parse.Try(data.FieldValue, out ByteParsedValue))
{
//TODO: Validate against Min and Max here
}
else
{
if (Parse.Try(data.FieldValue, out ChParsedValue))
{
//TODO: Validate against Min and Max here
}
else
{
if (data.FieldValue != null)
{
DataValid = false;
data.isValid = false;
}
}
}
break;
case "short":
short ShParsedValue;
short ShMaxValue, ShMinValue;
if (!Parse.Try(data.FieldValue, out ShParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out ShMinValue) &&
Parse.Try(data.FieldMaxValue, out ShMaxValue))
{
if (ShParsedValue < ShMinValue || ShParsedValue > ShMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "unsigned char":
case "unsignedchar":
byte UchParsedValue;
byte UchMaxValue, UchMinValue;
if (!Parse.Try(data.FieldValue, out UchParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out UchMinValue) &&
Parse.Try(data.FieldMaxValue, out UchMaxValue))
{
if (UchParsedValue < UchMinValue || UchParsedValue > UchMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "unsigned short":
case "unsignedshort":
ushort UshParsedValue;
ushort UshMaxValue, UshMinValue;
if (!Parse.Try(data.FieldValue, out UshParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out UshMinValue) &&
Parse.Try(data.FieldMaxValue, out UshMaxValue))
{
if (UshParsedValue < UshMinValue || UshParsedValue > UshMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "unsigned int":
case "unsignedint":
case "unsigned":
uint UiParsedValue;
uint UiMaxValue, UiMinValue;
if (!Parse.Try(data.FieldValue, out UiParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out UiMinValue) &&
Parse.Try(data.FieldMaxValue, out UiMaxValue))
{
if (UiParsedValue < UiMinValue || UiParsedValue > UiMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "float":
float FlParsedValue;
float FlMaxValue, FlMinValue;
if (!Parse.Try(data.FieldValue, out FlParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out FlMinValue) &&
Parse.Try(data.FieldMaxValue, out FlMaxValue))
{
if (FlParsedValue < FlMinValue || FlParsedValue > FlMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "double":
double DbParsedValue;
double DbMaxValue, DbMinValue;
if (!Parse.Try(data.FieldValue, out DbParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out DbMinValue) &&
Parse.Try(data.FieldMaxValue, out DbMaxValue))
{
if (DbParsedValue < DbMinValue || DbParsedValue > DbMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "long long":
case "longlong":
long LlParsedValue;
long LlMaxValue, LlMinValue;
if (!Parse.Try(data.FieldValue, out LlParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out LlMinValue) &&
Parse.Try(data.FieldMaxValue, out LlMaxValue))
{
if (LlParsedValue < LlMinValue || LlParsedValue > LlMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "unsigned long long":
case "unsignedlonglong":
ulong ULlParsedValue;
ulong ULlMaxValue, ULlMinValue;
if (!Parse.Try(data.FieldValue, out ULlParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out ULlMinValue) &&
Parse.Try(data.FieldMaxValue, out ULlMaxValue))
{
if (ULlParsedValue < ULlMinValue || ULlParsedValue > ULlMaxValue)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "boolean":
uint BParsedValue;
if (!Parse.Try(data.FieldValue, out BParsedValue))
{
DataValid = false;
data.isValid = false;
break;
}
if (Parse.Try(data.FieldMinValue, out UiMinValue) &&
Parse.Try(data.FieldMaxValue, out UiMaxValue))
{
if (BParsedValue < 0 || BParsedValue > 1)
{
DataValid = false;
data.isValid = false;
}
}
break;
case "address":
break;
default:
//This fn will not validate enumerations
if (!data.isEnum)
{
//Data is invalid because it is of an unknown type
DataValid = false;
data.isValid = false;
}
break;
}
}
return DataValid;
}
public bool CompareFieldValues(Message Msg)
{
Msg.SetEnumerationType(EnumerationType.EXPANDED_FIELDS);
foreach (MessageData Data in Msg)
{
foreach (MessageData ThisData in this)
{
if (ThisData.FieldName == Data.FieldName)
{
if (ThisData.FieldValue != Data.FieldValue)
return false;
break;
}
}
}
//If this function has not returned at this point than the Value fields are equal
return true;
}
#region Serialization/Deserialization for COE send
public byte[] serialize()
{
uint MsgSize = GetMessageSize();
uint MsgSizeOut = 0;
byte[] buffer = new byte[MsgSize];
//Write MessageData to OE Message Buffer
MsgDataToBuffer(ref buffer, out MsgSizeOut);
if (MsgSize != MsgSizeOut)
{
buffer = null; // error
throw new Exception("Message " + _name + ": serialization failed");
}
return buffer;
}
public bool deserialize(byte[] stream)
{
return BufferToMsgData(stream);
}
#endregion
#region ICloneable Members
public void RetrieveClone(Message clone)
{
_name = clone._name;
_label = clone._label;
Domain = clone.Domain;
_instruLabel = clone._instruLabel;
_packet = clone._packet;
_adapationStoreLabel = clone._adapationStoreLabel;
//Log = clone.Log;
Messages = clone.Messages;
_packing = clone._packing;
_count = clone._count;
m_Icd = clone.m_Icd;
if (clone.MessageDataArray == null)
{
MessageDataArray = null;
}
else
{
MessageDataArray = new MessageData[clone.MessageDataArray.Length];
for (int i = 0; i < clone.MessageDataArray.Length; i++)
{
MessageDataArray[i] = (MessageData)clone.MessageDataArray[i].Clone();
}
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public object Clone()
{
Message clone = new Message
{
_name = _name,
_label = _label,
Domain = Domain,
_instruLabel = _instruLabel,
_adapationStoreLabel = _adapationStoreLabel,
_packet = _packet,
/*clone.Log = Log;*/
Messages = Messages,
_packing = _packing,
_count = _count,
m_Icd = m_Icd,
MessageDataArray = CloneMessageData(MessageDataArray)
};
return clone;
}
/// <summary>
///
/// </summary>
/// <param name="orig"></param>
/// <returns></returns>
private MessageData[] CloneMessageData(MessageData[] orig)
{
MessageData[] clone = null;
if (orig != null)
{
clone = new MessageData[orig.Length];
for (int i = 0; i < orig.Length; i++)
{
clone[i] = (MessageData)orig[i].Clone();
}
}
return clone;
}
#endregion
}
}