305 lines
11 KiB
C#
305 lines
11 KiB
C#
// UNCLASSIFIED
|
|
/*-------------------------------------------------------------------------
|
|
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.
|
|
|
|
THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
|
|
GOVERNMENT.
|
|
|
|
UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
|
-------------------------------------------------------------------------*/
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using Raytheon.Common;
|
|
|
|
namespace Raytheon.Instruments
|
|
{
|
|
/// <summary>
|
|
/// Parse and Format Util for the DPF2 flow meter serial interface
|
|
/// </summary>
|
|
internal static class FlowMeterOmegaDPF20DataParser
|
|
{
|
|
#region PublicMembers
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
public struct HeaderStruct
|
|
{
|
|
public byte startOfFrame;//2
|
|
public byte frameType;//32,33,35,36,37,38
|
|
public byte reserved;//32
|
|
public byte orginAddress;//32 + real value
|
|
public byte destinationAddress;//32 + real value
|
|
public byte registerLocation;//32 + real value
|
|
public byte reserved2;//32
|
|
public byte dataLength;//num data bytes that follow
|
|
};
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
public struct ReadDisplayCmdStruct
|
|
{
|
|
public HeaderStruct header;
|
|
public FooterStruct footer;
|
|
};
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
public struct ReadDisplayRspStruct
|
|
{
|
|
public HeaderStruct header;
|
|
public byte byte1;
|
|
public byte byte2;
|
|
public byte byte3;
|
|
public byte byte4;
|
|
public byte byte5;
|
|
public byte byte6;
|
|
public byte byte7;
|
|
public byte byte8;
|
|
public FooterStruct footer;
|
|
};
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
public struct FooterStruct
|
|
{
|
|
public byte crc;
|
|
public byte endOfFrame;//3
|
|
};
|
|
|
|
#endregion
|
|
|
|
#region PublicFunctions
|
|
|
|
/// <summary>
|
|
/// Calculate the CRC per the DPF20 manual
|
|
/// </summary>
|
|
/// <param name="data">The data to run the crc on</param>
|
|
/// <param name="numBytesToProcess">The number of bytes to use for the crc</param>
|
|
/// <returns></returns>
|
|
public static byte PerformCrc(byte[] data, int numBytesToProcess)
|
|
{
|
|
byte crc = 0;
|
|
|
|
for (int i = 0; i < numBytesToProcess; i++)
|
|
{
|
|
crc = (byte)(crc ^ data[i]);
|
|
}
|
|
|
|
if (crc < 32)
|
|
{
|
|
crc = (byte)~crc;
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the byte array for the read display command
|
|
/// </summary>
|
|
/// <returns>a byte array that is the command to send to the meter to read the display</returns>
|
|
public static byte[] CreateReadFlowCmd(byte flowMeterAddress)
|
|
{
|
|
// populate the header per the manual
|
|
HeaderStruct header = new HeaderStruct();
|
|
header.startOfFrame = 2;
|
|
header.frameType = 36;
|
|
header.reserved = 32;
|
|
header.orginAddress = 32;
|
|
header.destinationAddress = (byte)(flowMeterAddress + 32);
|
|
header.registerLocation = 32;
|
|
header.reserved2 = 32;
|
|
header.dataLength = 32;
|
|
|
|
// populate the footer
|
|
FooterStruct footer = new FooterStruct();
|
|
footer.crc = 0;// set at end of function
|
|
footer.endOfFrame = 3;
|
|
|
|
// create the message
|
|
ReadDisplayCmdStruct cmd = new ReadDisplayCmdStruct();
|
|
cmd.header = header;
|
|
cmd.footer = footer;
|
|
|
|
// allocate the final buffer
|
|
int messageSize = (int)(System.Runtime.InteropServices.Marshal.SizeOf(typeof(ReadDisplayCmdStruct)));
|
|
byte[] buff = new byte[messageSize];
|
|
|
|
//Get a pointer to the Buffer
|
|
GCHandle pinnedArray = GCHandle.Alloc(buff, GCHandleType.Pinned);
|
|
|
|
//Pinned pointer to array
|
|
IntPtr pData = pinnedArray.AddrOfPinnedObject();
|
|
|
|
//Place message into buffer
|
|
Marshal.StructureToPtr(cmd, pData, true);
|
|
|
|
//Release array pointer
|
|
pinnedArray.Free();
|
|
|
|
// calculate and set crc
|
|
byte calculatedCrc = PerformCrc(buff, 8);
|
|
buff[8] = calculatedCrc;
|
|
|
|
return buff;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse out the flow from the read display response
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <param name="numBytes"></param>
|
|
/// <returns></returns>
|
|
public static double ParseReadFlowRsp(byte[] data, int numBytes)
|
|
{
|
|
const int DISPLAY_REGISTER = 32;
|
|
const int DATA_OFFSET = 32;
|
|
const int EXPECTED_MSG_ID = 37;
|
|
const int EXPECTED_DATA_LEN = 18;
|
|
|
|
if (numBytes < 18)
|
|
{
|
|
throw new Exception("Need at least " + EXPECTED_DATA_LEN + " to form the rsp message, received: " + numBytes);
|
|
}
|
|
|
|
// get a pointer to the data
|
|
GCHandle messagePinnedArray = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
IntPtr pMessageData = messagePinnedArray.AddrOfPinnedObject();
|
|
|
|
// populate the structure
|
|
ReadDisplayRspStruct msgStruct = (ReadDisplayRspStruct)Marshal.PtrToStructure(pMessageData, typeof(ReadDisplayRspStruct));
|
|
|
|
// free the ptr
|
|
messagePinnedArray.Free();
|
|
|
|
if (msgStruct.header.frameType != EXPECTED_MSG_ID)
|
|
{
|
|
throw new Exception("expected msg id: " + EXPECTED_MSG_ID + ", received: " + msgStruct.header.frameType);
|
|
}
|
|
|
|
// subtract the DATA_OFFSET because that is what the manual says to do:)
|
|
int numDataBytes = msgStruct.header.dataLength - DATA_OFFSET;
|
|
|
|
int headerSize = (int)(System.Runtime.InteropServices.Marshal.SizeOf(typeof(HeaderStruct)));
|
|
|
|
byte calculatedCrc = PerformCrc(data, numDataBytes + headerSize);
|
|
byte receivedCrc = msgStruct.footer.crc;
|
|
if (calculatedCrc != receivedCrc)
|
|
{
|
|
throw new Exception("calculated crc: " + calculatedCrc + ", is not equal to the received crc: " + receivedCrc);
|
|
}
|
|
|
|
// confirm the from register
|
|
int fromRegister = msgStruct.header.registerLocation;
|
|
if (fromRegister != DISPLAY_REGISTER)
|
|
{
|
|
throw new Exception("from register: " + fromRegister + ", is not the expected register: " + DISPLAY_REGISTER);
|
|
}
|
|
|
|
// get the data
|
|
byte[] dataPayload = new byte[numDataBytes];
|
|
Array.Copy(data, headerSize, dataPayload, 0, numDataBytes);
|
|
|
|
// convert it to a string
|
|
string rspStr = Encoding.ASCII.GetString(dataPayload);
|
|
|
|
//Convert string value to double and return
|
|
return double.Parse(rspStr);
|
|
}
|
|
|
|
/// <summary>
|
|
/// parse out flow from the master flow message (ID 35)
|
|
/// </summary>
|
|
/// <returns>the flow rate.</returns>
|
|
public static double ParseMasterFlowMsg(byte[] data, int numBytes)
|
|
{
|
|
const int DISPLAY_REGISTER = 32;
|
|
const int DATA_OFFSET = 32;
|
|
const int EXPECTED_MSG_ID = 35;
|
|
const int EXPECTED_DATA_LEN = 18;
|
|
|
|
// start of message data pattern
|
|
byte[] startOfMesage = new byte[] { 2, 35 };
|
|
|
|
// data search index
|
|
int searchIndex = 0;
|
|
|
|
// need at least 18 bytes
|
|
if (numBytes < EXPECTED_DATA_LEN)
|
|
{
|
|
throw new Exception("Need at least " + EXPECTED_DATA_LEN + " to form the rsp message, received: " + numBytes);
|
|
}
|
|
// if there are more than two messages, just need the end of the buffer
|
|
// worst case scenario for 1 complete message would be 35 bytes (17 from an incomplete message and then 18 for the complete one)
|
|
else if (numBytes >= (EXPECTED_DATA_LEN * 2) - 1)
|
|
{
|
|
searchIndex = numBytes - ((EXPECTED_DATA_LEN * 2) - 1);
|
|
}
|
|
|
|
int finalMsgStartIndex = Util.ByteArraySearch(data, searchIndex, startOfMesage);
|
|
|
|
if (finalMsgStartIndex == -1)
|
|
{
|
|
throw new Exception("Could not find start of message");
|
|
}
|
|
else if((numBytes - finalMsgStartIndex) < EXPECTED_DATA_LEN)
|
|
{
|
|
throw new Exception("Found start of message, but not enough bytes for a complete message");
|
|
}
|
|
|
|
byte[] messagePayload = new byte[EXPECTED_DATA_LEN];
|
|
Array.Copy(data, finalMsgStartIndex, messagePayload, 0, EXPECTED_DATA_LEN);
|
|
|
|
// get a pointer to the data
|
|
GCHandle messagePinnedArray = GCHandle.Alloc(messagePayload, GCHandleType.Pinned);
|
|
IntPtr pMessageData = messagePinnedArray.AddrOfPinnedObject();
|
|
|
|
// populate the structure
|
|
HeaderStruct headerStruct = (HeaderStruct)Marshal.PtrToStructure(pMessageData, typeof(HeaderStruct));
|
|
|
|
// free the ptr
|
|
messagePinnedArray.Free();
|
|
|
|
if (headerStruct.frameType != EXPECTED_MSG_ID)
|
|
{
|
|
throw new Exception("expected msg id: " + EXPECTED_MSG_ID + ", received: " + headerStruct.frameType);
|
|
}
|
|
|
|
// subtract the DATA_OFFSET because that is what the manual says to do:)
|
|
int numDataBytes = headerStruct.dataLength - DATA_OFFSET;
|
|
|
|
int headerSize = (int)(System.Runtime.InteropServices.Marshal.SizeOf(typeof(HeaderStruct)));
|
|
|
|
// confirm the crc
|
|
byte calculatedCrc = PerformCrc(messagePayload, numDataBytes + headerSize);
|
|
byte receivedCrc = messagePayload[numDataBytes + headerSize];
|
|
if (calculatedCrc != receivedCrc)
|
|
{
|
|
throw new Exception("calculated crc: " + calculatedCrc + ", is not equal to the received crc: " + receivedCrc);
|
|
}
|
|
|
|
// confirm the from register
|
|
int fromRegister = headerStruct.registerLocation;
|
|
if (fromRegister != DISPLAY_REGISTER)
|
|
{
|
|
throw new Exception("from register: " + fromRegister + ", is not the expected register: " + DISPLAY_REGISTER);
|
|
}
|
|
|
|
// get the data
|
|
byte[] dataPayload = new byte[numDataBytes];
|
|
Array.Copy(messagePayload, headerSize, dataPayload, 0, numDataBytes);
|
|
|
|
// convert to a string
|
|
string rspStr = Encoding.ASCII.GetString(dataPayload);
|
|
|
|
//Convert string value to double and return
|
|
return double.Parse(rspStr);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|