private EchoStatus ProcessEcho(int echoLength)
        {
            if (port == null || messageProcessor == null)
            {
                throw new InvalidOperationException();
            }

            var data = ReadData(echoLength, true);

            if (data.Length == 0)
            {
                logger.ErrorFormat("No data read from port");
                return(EchoStatus.None);
            }

            // if the first byte is a NAK (15) then return a NAK and add whatever additional data was read to the buffer
            if (data[0] == (byte)EchoStatus.NAK)
            {
                logger.DebugFormat("RX: {0}", Utilities.ByteArrayToString(data));
                if (data.Length > 1)
                {
                    int remainingCount = data.Length - 1;
                    var remainingData  = new byte[remainingCount];
                    Array.Copy(data, 1, remainingData, 0, remainingCount);
                    lock (buffer)
                        buffer.AddRange(remainingData);
                    ProcessData(); //process the rest of the data stream
                }
                return(EchoStatus.NAK);
            }

            // scan until a MESSAGE START byte (02) is detected, which should be the first byte
            int offset = 0;

            while (offset < data.Length)
            {
                if (data[offset++] == Constants.MessageStartByte)
                {
                    break;
                }
            }

            // exit if no MESSAGE START byte detected
            if (offset >= data.Length)
            {
                logger.DebugFormat("RX: {0} ERROR - Failed to find MESSAGE START byte (02)", Utilities.ByteArrayToString(data));
                return(EchoStatus.Unknown);
            }

            logger.DebugFormat("RX: {0}", Utilities.ByteArrayToString(data));

            // warn about any skipped bytes
            if (offset > 1)
            {
                logger.WarnFormat("Skipping {0} bytes to '{1}', discarded '{2}'", offset - 1, Utilities.ByteArrayToString(data, offset - 1), Utilities.ByteArrayToString(data, 0, offset - 1));
            }

            // process the echo and decode the trailing status byte
            int count;

            if (messageProcessor.ProcessEcho(data, offset, out count))
            {
                int  j      = offset + count;
                byte result = j < data.Length ? data[j] : Byte.MinValue;
                j += 1;
                if (data.Length > j) // if there's data beyond the echo then add it to the buffer
                {
                    int remainingCount = data.Length - j;
                    var remainingData  = new byte[remainingCount];
                    Array.Copy(data, j, remainingData, 0, remainingCount);
                    lock (buffer)
                        buffer.AddRange(remainingData);
                    ProcessData(); //process the rest of the data stream
                }
                if (result == (byte)EchoStatus.ACK)
                {
                    messageProcessor.SetEchoStatus(EchoStatus.ACK);
                    logger.DebugFormat("PLM: {0} ACK", Utilities.ByteArrayToString(data, offset - 1, count + 2)); // +1 for MESSAGE START byte (02), +1 for ACK byte (06)
                    return(EchoStatus.ACK);
                }
                if (result == (byte)EchoStatus.NAK)
                {
                    logger.DebugFormat("PLM: {0} NAK", Utilities.ByteArrayToString(data, offset - 1, count + 2)); // +1 for MESSAGE START byte (02), +1 for NAK byte (15)
                    messageProcessor.SetEchoStatus(EchoStatus.NAK);
                    return(EchoStatus.NAK);
                }
                logger.DebugFormat("PLM: {0} Unknown trailing byte", Utilities.ByteArrayToString(data, offset - 1, count + 2)); // +1 for MESSAGE START byte (02), +1 for unknown byte
                messageProcessor.SetEchoStatus(EchoStatus.Unknown);
                return(EchoStatus.Unknown);
            }
            logger.DebugFormat("PLM: {0} Echo mismatch", Utilities.ByteArrayToString(data, offset - 1));
            return(EchoStatus.Unknown);
        }