// 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 { /// /// Parse and Format Util for the DPF2 flow meter serial interface /// 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 /// /// Calculate the CRC per the DPF20 manual /// /// The data to run the crc on /// The number of bytes to use for the crc /// 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; } /// /// Creates the byte array for the read display command /// /// a byte array that is the command to send to the meter to read the display 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; } /// /// Parse out the flow from the read display response /// /// /// /// 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); } /// /// parse out flow from the master flow message (ID 35) /// /// the flow rate. 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 } }