借鑒之前的Pipeline的操作方式,現在目標是給串口讀取操作也使用上Pipeline,稍微改造一下,以下代碼可以直接運行,
協議為使用連續的4個0XFF作為結尾,沒有頭標志,資料總長為68位定長,
我需要判斷從開始到選定位置是否長度足夠,這里面用來判斷segment長度我用了這個方式,
buffer.Slice(0, start.Value).Length >= 64
其實最早不是使用這個東西的,而是使用的SequencePosition的GetInteger()方法,獲取到了位置的index,自然就知道了長度等資訊,而且非常方便進行截取操作,可是在使用的時候,發現一個非常詭異的問題:通過這個方法獲取到的index值要大于Buffer的總長度,Slice直接彈出ArgumentOutOfRangeException,但是不彈出錯誤,除錯的時候非常麻煩,
查看這個方法定義的時候,發現簽名是這樣的:
[EditorBrowsable(EditorBrowsableState.Never)]
public int GetInteger();
這個東西第一次見到,VS并不會提示,但是你強行寫的話,能夠正常編譯,看來微軟并不是很像讓我們看到這個玩意,仔細挖掘一下,發現通過PositionOf方法獲得的SequencePosition內部參考了一段長度為4096的記憶體,這個GetInteger()有時候回傳的是在這段Memory上面的Index值,

看來這個東西是內部使用的,不太推薦我們使用,
有幾個問答也說到了這個事情:問答1,問答2
我們只能使用GetPosition方法來獲得相對的位置,不過我用的這個設備,協議是尾部標志,如果使用PostionOf的話,偏移量得是負數,在嘗試了很多次不通之后,發現微軟檔案中有說到這個潛在問題:
- SequencePosition 是特定 ReadOnlySequence
的位置標記,而不是絕對位置, 由于它是相對于特定 ReadOnlySequence 的,因此如果在其起源的 ReadOnlySequence 之外使用,則沒有意義, - 不能對沒有 ReadOnlySequence
的 SequencePosition 執行算術運算, 這意味著,執行 position++ 等基本操作將以 ReadOnlySequence .GetPosition(position, 1) 的形式寫入, - GetPosition(long) 不支持負索引, 這意味著,如果沒有遍歷所有段,就無法獲取倒數第二個字符,
后面還有一些我就不貼了,總之,那我這種情況只能使用buffer.Slice(0, pos)獲得最長的片段,并將內容傳輸給處理程式進行,
總之,不要使用操作ReadOnlySpan
為了解決操作的復雜性,.NET Core 3.0引入了一個
SequenceReader<T>簡化了操作,以后有機會使用的時候在寫吧,
最后程式如下:
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<byte> 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<byte> 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();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/250416.html
標籤:C#
下一篇:進口潛水電泵原理特點及品牌(圖)
