2310 lines
84 KiB
C#
2310 lines
84 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 System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
|
|
namespace Raytheon.Common.Coe
|
|
{
|
|
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 static 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";
|
|
|
|
public bool LogData { get; set; }
|
|
|
|
// Default Logger object
|
|
//public static Logger m_DefaultLog = new Logger();
|
|
|
|
private Message()
|
|
{
|
|
LogData = true;
|
|
}
|
|
private Message(MessageXmlDocument MessageDatabase)
|
|
{
|
|
LogData = true;
|
|
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, bool logData = true)
|
|
{
|
|
LogData = logData;
|
|
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
|
|
{
|
|
messageDatabase.LogData = LogData;
|
|
string messageSpecificationString = messageDatabase.GetMessage(messageName);
|
|
if (messageSpecificationString == null)
|
|
{
|
|
throw new Exception(messageName + " Message not found in the ICD.");
|
|
}
|
|
else
|
|
{
|
|
Messages = messageDatabase.CreateNavigator();
|
|
_name = messageName;
|
|
|
|
ParseMessageString(messageSpecificationString);
|
|
|
|
if (LogData)
|
|
_logger.Debug("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, bool logData = true)
|
|
{
|
|
LogData = logData;
|
|
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();
|
|
|
|
ParseMessageString(messageSpecificationString);
|
|
|
|
if (LogData)
|
|
_logger.Debug("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);
|
|
// remove namespace
|
|
fieldType = Regex.Replace(fieldType, @"[^:]+::([^:]+)", "$1");
|
|
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, LogData);
|
|
//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 "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 "unsigned char":
|
|
case "unsignedchar":
|
|
case "uint8_t":
|
|
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 "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 short":
|
|
case "unsignedshort":
|
|
case "uint16_t":
|
|
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 "int":
|
|
case "int32_t":
|
|
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 "unsigned int":
|
|
case "unsignedint":
|
|
case "unsigned":
|
|
case "uint32_t":
|
|
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":
|
|
case "int64_t":
|
|
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":
|
|
case "uint64_t":
|
|
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);
|
|
}
|
|
else
|
|
throw new Exception($"Type {data.FieldType} is invalid in COE message");
|
|
|
|
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 "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 "unsigned char":
|
|
case "unsignedchar":
|
|
case "uint8_t":
|
|
BitFieldGeneric<byte> SBValue = new BitFieldGeneric<byte>
|
|
(DataBuffer[index], data.bitMask);
|
|
data.FieldValue = SBValue.ToString(); /*ACB*/
|
|
//data.FieldValue = DataBuffer[Index].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 short":
|
|
case "unsignedshort":
|
|
case "uint16_t":
|
|
BitFieldGeneric<ushort> USValue = new BitFieldGeneric<ushort>
|
|
(BitConverter.ToUInt16(DataBuffer, index), data.bitMask);
|
|
data.FieldValue = USValue.ToString();
|
|
break;
|
|
case "int":
|
|
case "int32_t":
|
|
BitFieldGeneric<int> IValue = new BitFieldGeneric<int>
|
|
(BitConverter.ToInt32(DataBuffer, index), data.bitMask);
|
|
IValue.ToSigned();
|
|
data.FieldValue = IValue.ToString();
|
|
break;
|
|
case "unsigned int":
|
|
case "unsignedint":
|
|
case "uint32_t":
|
|
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":
|
|
case "int64_t":
|
|
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":
|
|
case "uint64_t":
|
|
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();
|
|
}
|
|
else
|
|
throw new Exception($"Type {data.FieldType} is invalid in COE message");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get parameters in the message
|
|
/// </summary>
|
|
public List<KeyValuePair<string, string>> GetParameters()
|
|
{
|
|
List<KeyValuePair<string, string>> parms = new List<KeyValuePair<string, string>>();
|
|
|
|
foreach (var item in MessageDataArray.Where(m => !m.FieldName.StartsWith("$")))
|
|
{
|
|
parms.Add(new KeyValuePair<string, string>(item.FieldName, item.FieldValue));
|
|
if (item.MessageArray != null && item.MessageArray.Any())
|
|
{
|
|
var innerParams = GetParameters(item.MessageArray);
|
|
if (innerParams != null)
|
|
{
|
|
parms.AddRange(innerParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
return parms;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get parameters recursively in the message
|
|
/// </summary>
|
|
private List<KeyValuePair<string, string>> GetParameters(MessageData[] array)
|
|
{
|
|
List<KeyValuePair<string, string>> parms = new List<KeyValuePair<string, string>>();
|
|
|
|
foreach (var item in array.Where(m => !m.FieldName.StartsWith("$")))
|
|
{
|
|
parms.Add(new KeyValuePair<string, string>(item.FieldName, item.FieldValue));
|
|
if (item.MessageArray != null && item.MessageArray.Any())
|
|
{
|
|
var innerParams = GetParameters(item.MessageArray);
|
|
if (innerParams != null)
|
|
{
|
|
parms.AddRange(innerParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
return parms;
|
|
}
|
|
|
|
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, bool logData = true)
|
|
{
|
|
uint size = 4;
|
|
|
|
switch (Type)
|
|
{
|
|
case "char":
|
|
case "unsigned char":
|
|
case "unsignedchar":
|
|
case "uint8_t":
|
|
size = (uint)TypeSize.CHAR;
|
|
break;
|
|
|
|
case "short":
|
|
case "unsigned short":
|
|
case "unsignedshort":
|
|
case "uint16_t":
|
|
size = (uint)TypeSize.SHORT;
|
|
break;
|
|
|
|
case "int":
|
|
case "int32_t":
|
|
case "unsigned int":
|
|
case "unsignedint":
|
|
case "unsigned":
|
|
case "uint32_t":
|
|
case "address":
|
|
case "boolean":
|
|
case "float":
|
|
size = (uint)TypeSize.INT;
|
|
break;
|
|
|
|
case "double":
|
|
case "long long":
|
|
case "longlong":
|
|
case "unsigned long long":
|
|
case "unsignedlonglong":
|
|
case "uint64_t":
|
|
case "int64_t":
|
|
size = (uint)TypeSize.LONGLONG;
|
|
break;
|
|
|
|
default:
|
|
if (IsEnum == true)
|
|
size = (uint)TypeSize.ENUM;
|
|
else
|
|
{
|
|
if (logData)
|
|
_logger.Debug($"Type {Type} is not recognized as basic 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;
|
|
throw new Exception($"Type {data.FieldType} is invalid in COE message");
|
|
}
|
|
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
|
|
}
|
|
}
|