// ********************************************************************************************************** // 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 m_MsgNameMap = null; private Dictionary 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(); m_MsgLabelMap = new Dictionary(); } } 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(); m_MsgLabelMap = new Dictionary(); } 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."); } } } } } /// /// This constructor populates the Message based on the Message Label /// instead of the Message Name /// /// /// 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(); m_MsgLabelMap = new Dictionary(); } 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 length = new LinkedList(); // // 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 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; } /// /// logs message with all the fields /// /// /// 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 indexStack = new Stack(); private readonly Stack messageStack = new Stack(); 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 iParsedValue = new BitFieldGeneric(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 sbParsedValue = new BitFieldGeneric (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 sParsedValue = new BitFieldGeneric(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 sbParsedValue = new BitFieldGeneric(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 usParsedValue = new BitFieldGeneric(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 uiParsedValue = new BitFieldGeneric(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 llParsedValue = new BitFieldGeneric(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 ullParsedValue = new BitFieldGeneric(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 eParsedValue = new BitFieldGeneric(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 enums = m_Icd.GetEnumerations(Type); foreach (KeyValuePair 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 IValue = new BitFieldGeneric (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 CValue = new BitFieldGeneric ((sbyte)DataBuffer[index], data.bitMask); CValue.ToSigned(); data.FieldValue = CValue.ToString(); } break; case "short": BitFieldGeneric SValue = new BitFieldGeneric (BitConverter.ToInt16(DataBuffer, index), data.bitMask); SValue.ToSigned(); data.FieldValue = SValue.ToString(); break; case "unsigned char": case "unsignedchar": BitFieldGeneric SBValue = new BitFieldGeneric (DataBuffer[index], data.bitMask); data.FieldValue = SBValue.ToString(); /*ACB*/ //data.FieldValue = DataBuffer[Index].ToString(); break; case "unsigned short": case "unsignedshort": BitFieldGeneric USValue = new BitFieldGeneric (BitConverter.ToUInt16(DataBuffer, index), data.bitMask); data.FieldValue = USValue.ToString(); break; case "unsigned int": case "unsignedint": case "unsigned": case "address": case "boolean": BitFieldGeneric UIValue = new BitFieldGeneric (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 LLValue = new BitFieldGeneric (BitConverter.ToInt64(DataBuffer, index), data.bitMask); LLValue.ToSigned(); data.FieldValue = LLValue.ToString(); break; case "unsigned long long": case "unsignedlonglong": BitFieldGeneric ULLValue = new BitFieldGeneric (BitConverter.ToUInt64(DataBuffer, index), data.bitMask); data.FieldValue = ULLValue.ToString(); break; default: if (data.isEnum) { BitFieldGeneric EnumValue = new BitFieldGeneric (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(); } } } /// /// /// /// 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; } /// /// /// /// /// 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 } }