Re[2]: C# SerialPort Неполный прием сообщений
От: Neco  
Дата: 22.05.13 19:42
Оценка: 3 (1)
пример

    class Program {
        static void Main(string[] args) {
            TestVaryLength(new string[] { "123456789" }, new int[] { 4, 9 });
            TestVaryLength(new string[] { "123456789" }, new int[] { 4, 4, 4, 1 });
            TestVaryLength(new string[] { "123456789" }, new int[] { 3, 5, 4, 1 });

            TestVaryLength(new string[] { "123456789", "123" }, new int[] { 3, 5, 4, 8 });
            TestVaryLength(new string[] { "123456789", "123" }, new int[] { 3, 5, 4, 1, 5, 2 });

            TestVaryLength(new string[] { "123456789", "123", "12345" }, new int[] { 3, 5, 4, 17 });
            TestVaryLength(new string[] { "123456789", "123", "12345" }, new int[] { 3, 5, 4, 1, 16 });
            TestVaryLength(new string[] { "123456789", "123", "12345" }, new int[] { 3 + 5 + 4 + 1 + 16 });
            TestVaryLength(new string[] { "123456789", "123", "12345" }, new int[] { 3 + 5 + 4 + 1 + 15, 1 });
        }

        private static void TestVaryLength(string[] textArray, int[] partsSize) {
            SerialPortEmulator serialPort = new SerialPortEmulator();

            IMessageReader msgRdr = new MessageReaderVaryLength(serialPort);

            Queue<string> expected = new Queue<string>(textArray);
            msgRdr.MessageArrived += (sender, e) => {
                var expText = expected.Dequeue();
                var actText = BytesToString(e.Data);
                Test(expText, actText);
            };
            List<byte> bufferPrep = new List<byte>();
            foreach (var oneText in textArray) {
                var buf = StringToBytes(oneText);
                bufferPrep.AddRange(BitConverter.GetBytes(buf.Length));
                bufferPrep.AddRange(buf);
            }
            var buffer = bufferPrep.ToArray();
            int written = 0;
            foreach (var onePartSize in partsSize) {
                serialPort.PutData(TakePart(buffer, written, onePartSize));
                serialPort.RaiseDataReceived();
                written += onePartSize;
            }
            if (written != buffer.Length)
                throw new InvalidOperationException(string.Format("The test is not conformed well. Written [{0}] bytes, in buffer [{1}] bytes.", written, buffer.Length));

            if (expected.Count > 0)
                throw new InvalidOperationException("Test failed - not all elements were read.");

            Console.WriteLine("Ok");
        }
        private static string BytesToString(byte[] array) {
            return Encoding.UTF8.GetString(array);
        }
        private static byte[] StringToBytes(string text) {
            return Encoding.UTF8.GetBytes(text);
        }
        private static byte[] TakePart(byte[] buffer, int offset, int count) {
            var rez = new byte[count];
            Array.Copy(buffer, offset, rez, 0, count);
            return rez;
        }

        private static void Test(byte[] expected, byte[] actual) {
            if (!Array.Equals(actual, expected)) {
                throw new InvalidOperationException("Arrays are not equal.");
            }
        }
        private static void Test(string expected, string actual) {
            if (string.Compare(actual, expected,  StringComparison.Ordinal) != 0) {
                throw new InvalidOperationException(string.Format("Expected [{0}], actual [{1}].", expected, actual));
            }
        }
    
    }
    interface ISerialPort {
        event EventHandler DataReceived;
        int Read(byte[] buffer, int offset, int count);
        int ReadInt32();
        int BytesToRead { get; }
    }
    class SerialPortEmulator : ISerialPort {
        private List<byte> _allData = new List<byte>();
        public event EventHandler DataReceived;

        public int Read(byte[] buffer, int offset, int count) {
            int allDataLength = _allData.Count;
            int factCount = Math.Min(allDataLength, count);

            _allData.CopyTo(0, buffer, offset, factCount);

            _allData.RemoveRange(0, factCount);

            return factCount;
        }
        public int ReadInt32() {
            int intSize = sizeof(int);
            if (_allData.Count < intSize)
                throw new InvalidOperationException(string.Format("Cannot read int from the array which contains [{0}] bytes.", _allData.Count));

            byte[] tmpBuf = new byte[intSize];
            Read(tmpBuf, 0, intSize);
            return BitConverter.ToInt32(tmpBuf, 0);
        }

        public void PutData(byte[] data) {
            _allData.AddRange(data);
        }
        public void RaiseDataReceived() {
            var handler = DataReceived;
            if (handler != null) {
                handler(this, EventArgs.Empty);
            }
        }
        public int BytesToRead {
            get { return _allData.Count; }
        }
    }
    interface IMessageReader {
        event EventHandler<MessageArrivedEventArgs> MessageArrived;
    }
    class MessageArrivedEventArgs : EventArgs {
        public MessageArrivedEventArgs(byte[] data) {
            Data = data;
        }
        public byte[] Data { get; private set; }
    }
    class MessageReaderVaryLength : IMessageReader {
        private ISerialPort _serialPort;
        private const int SMALL_BUF_SIZE = 5;
        private byte[] _buffer;
        private int _bytesHave;
        private int _expectedSize;
        private State _state;

        public event EventHandler<MessageArrivedEventArgs> MessageArrived;

        public MessageReaderVaryLength(ISerialPort serialPort) {
            _state = State.ReadingSize;
            _serialPort = serialPort;
            _serialPort.DataReceived += new EventHandler(_serialPort_DataReceived);
        }

        void _serialPort_DataReceived(object sender, EventArgs e) {
            int bytesToRead = _serialPort.BytesToRead;
            bool exitLoop = false;
            while (!exitLoop) {
                switch (_state) {
                    case State.ReadingSize: {
                            if (bytesToRead >= 4) {
                                _expectedSize = _serialPort.ReadInt32();
                                _buffer = new byte[_expectedSize];
                                _bytesHave = 0;
                                bytesToRead -= 4;
                                _state = State.ReadingMessage;
                            } else {
                                exitLoop = true;
                            }
                            break;
                        }
                    case State.ReadingMessage: {
                            int sizeToRead = _expectedSize - _bytesHave;
                            int factRead = _serialPort.Read(_buffer, _bytesHave, sizeToRead);
                            _bytesHave += factRead;
                        
                            if (_bytesHave > _expectedSize)
                                throw new InvalidOperationException("Something went wrong - have read more than expected.");

                            if (_bytesHave == _expectedSize) {
                                OnMessageArrived(_buffer);
                                _state = State.ReadingSize;
                            }

                            bytesToRead -= factRead;
                            break;
                        }
                    default: throw new InvalidOperationException(string.Format("Unknown state [{0}]", _state));
                }
                
                if (bytesToRead < 0)
                    throw new InvalidOperationException("Something went wrong - 'bytes to read' is less than zero.");

                if (bytesToRead == 0)
                    exitLoop = true;
            }
        }
        private void OnMessageArrived(byte[] buffer) {
            var handler = MessageArrived;
            if (handler != null) {
                handler(this, new MessageArrivedEventArgs(buffer));
            }
        }

        private enum State {
            ReadingSize, ReadingMessage
        }

    }
всю ночь не ем, весь день не сплю — устаю
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.