Strange readonlysequence problem when using pipeline on serial port

Time:2021-8-26

Learn from the previous pipelineOperation modeNow, the goal is to use pipeline for serial port read operation. With a little modification, the following code can be run directly.

The protocol uses four consecutive 0xff as the end without header flag. The total length of data is 68 bits fixed length.

I need to judge whether the length from the beginning to the selected position is enough, which is used to judgesegmentI used this way.

buffer.Slice(0, start.Value).Length >= 64

In fact, this thing was not used in the first place, but the getinteger () method of sequenceposition was used to obtain the index of the location. Naturally, you know the length and other information, and it is very convenient for interception. However, a very strange problem is found when using this method: the index value obtained by this method is greater than the total length of the buffer. Slice pop-upArgumentOutOfRangeException, but it does not pop up errors. Debugging is very troublesome.

When viewing the method definition, we find that the signature is as follows:

[EditorBrowsable(EditorBrowsableState.Never)]
public int GetInteger();

The first time I saw this thing, vs will not prompt, but if you force it to write, it can be compiled normally. It seems that Microsoft is not very like showing us this thing. Dig carefully and find that throughPositionOfMethod obtainedSequencePositionAn internal reference is made to a memory with a length of 4096. Sometimes this getinteger () returns the index value above this memory.

It seems that this thing is used internally and is not recommended for us.

Several Q & A also talked about this matter:Q & A 1Q & A 2

We can only useGetPositionMethod to obtain the relative position. However, for the device I use, the protocol is the tail flag. If it is usedPostionOfIf so, the offset must be negative. After trying many times, I found that MicrosoftfileThere’s something about this in the bookPotential problems:

  • Sequenceposition is a specific readonlysequenceThe position mark of the, not the absolute position. Because it is relative to a specific readonlysequenceSo if the readonlysequence at its originIt makes no sense to use it outside.
  • Can’t do without readonlysequenceThe sequenceposition of performs an arithmetic operation. This means that basic operations such as position + + will be executed with readonlysequence. getposition (position, 1).
  • Getposition (long) does not support negative indexes. This means that you cannot get the penultimate character without traversing all segments.
    I won’t post some in the back, anyway. Then I can only use it in this casebuffer.Slice(0, pos)Get the longest fragment and transfer the content to the handler for processing.

Anyway,Do not use the action readonlyspan/ReadOnlyMemoryTo operate readonlysequence/SequencePosition!

To address the complexity of operations,. Net core 3.0 introduces aSequenceReaderSimplify the operation. Write it when you have a chance to use it in the future.

The final procedure is as follows:

private async void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort p = sender as SerialPort;
            byte[] bytes = new byte[1024 * 4];
            var dataCount = p.Read(bytes, 0, p.BytesToRead);
            var span = bytes.AsMemory().Slice(0, dataCount);
            await FillPipeAsync(span);
        }

private void InitPipe()
        {
            Pipe pipe = new Pipe();
            writer = pipe.Writer;
            //Task writing = FillPipeAsync(port, pipe.Writer);
            Task.Run(() => ReadPipeAsync(pipe.Reader));
            //await Task.WhenAll(reading, writing);
        }

        private PipeWriter writer;

        private async Task FillPipeAsync(ReadOnlyMemory memory)
        {
            await writer.WriteAsync(memory);
            //writer.Advance(memory.Length);

            // Make the data available to the PipeReader
            FlushResult result = await writer.FlushAsync();
            if (result.IsCompleted)
                writer.Complete();
        }

        private async Task ReadPipeAsync(PipeReader reader)
        {
            while (true)
            {
                try
                {
                    ReadResult result = await reader.ReadAsync();
                    ReadOnlySequence buffer = result.Buffer;

                    SequencePosition? start = null;
                    var headBytes = new byte[] { 0xff, 0xff, 0xff, 0xff };

                    do
                    {


                        // Find the EOL
                        start = buffer.PositionOf(headBytes[0]);

                        if (start != null)
                        {
                            if (buffer.Slice(start.Value).Length >= 4)
                            {
                                var headtoCheck = buffer.Slice(start.Value, 4).ToArray();
                                if (headtoCheck.SequenceEqual(headBytes))
                                {
                                    if(buffer.Slice(0, start.Value).Length >= 64)
                                    {
                                        var pos = buffer.GetPosition(4, start.Value);
                                        var mes = buffer.Slice(0, pos);
                                        DataProcess.Process(mes.ToArray());
                                        var next = buffer.GetPosition(4, start.Value);
                                        buffer = buffer.Slice(next);
                                    }
                                    else
                                    {
                                        var next = buffer.GetPosition(4, start.Value);
                                        buffer = buffer.Slice(next);
                                    }
                                }
                                else
                                {
                                    var next = buffer.GetPosition(1, start.Value);
                                    buffer = buffer.Slice(next);
                                }
                            }
                            else
                            {
                                var next = buffer.GetPosition(1, start.Value);
                                buffer = buffer.Slice(next);
                            }
                        }


                    }
                    while (start != null);
                    // We sliced the buffer until no more data could be processed
                    // Tell the PipeReader how much we consumed and how much we left to process

                    if (result.IsCompleted)
                    {
                        continue;
                    }
                    reader.AdvanceTo(buffer.Start, buffer.End);
                }
                catch (ArgumentOutOfRangeException e)
                {

                    //throw e;
                }
            }
            reader.Complete();
        }

Recommended Today

Java Engineer Interview Questions

The content covers: Java, mybatis, zookeeper, Dubbo, elasticsearch, memcached, redis, mysql, spring, spring boot, springcloud, rabbitmq, Kafka, Linux, etcMybatis interview questions1. What is mybatis?1. Mybatis is a semi ORM (object relational mapping) framework. It encapsulates JDBC internally. During development, you only need to pay attention to the SQL statement itself, and you don’t need to […]