借鉴之前的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/ReadOnlyMemory的思路来操作ReadOnlySequence/SequencePosition!

为了解决操作的复杂性,.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();
}

串口使用Pipeline时诡异的ReadOnlySequence问题的更多相关文章

  1. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  2. 【Debug】串口发送数据时部分字节被拉长,出现帧错误,原因MCU进入低功耗模式导致串口时钟停了!

    串口发送数据时部分字节被拉长,出现帧错误,原因MCU进入低功耗模式导致串口时钟停了!

  3. 纪念一下学写pipeline时脑子里的坑

    用的是filespipeline,用的存储地址是images的地址 测试煎蛋ooxx首页,shell测试的时候返回很多列表,但是实际爬的时候一直只返回一条,很烦,一直测一直测,就是不行,后来才发现,首 ...

  4. [连载]《C#通讯(串口和网络)框架的设计与实现》-2.框架的总体设计

    目       录 C#通讯(串口和网络)框架的设计与实现... 1 (SuperIO)- 框架的总体设计... 1 第二章           框架总体的设计... 2 2.1           ...

  5. [连载]《C#通讯(串口和网络)框架的设计与实现》- 5.串口和网络统一IO设计

    目       录 第五章           串口和网络统一IO设计... 2 5.1           统一IO接口... 2 5.1.1    串口IO.. 4 5.1.2    网络IO.. ...

  6. 使用Java实现简单串口通信

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar ...

  7. Windows驱动——虚拟机 虚拟串口 双机调试

    =================================版权声明================================= 版权声明:原创文章 谢绝转载  请通过右侧公告中的“联系邮 ...

  8. mfc 调用Windows的API函数实现同步异步串口通信(源码)

    在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信方便易行,应用广泛. 一般情况下,工控机和各智能仪表通过RS485总线进行通信.RS485的通信方式是半 ...

  9. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

随机推荐

  1. Linux下yum下载依赖包

    先安装依赖包yum-plugin-downloadonly [root@node1 ~]# yum install yum-plugin-downloadonly 方法一: [root@node1 ~ ...

  2. latex参考文献删除[.s.l],[.s.n]

    用latex写毕业论文的插入会议参考论文的时候可能会遇到编译后的文档里面一堆[.s.l],[.s.n]的问题. 这是因为ref里面会议条目找不到地址信息,所以用[.s.l],[.s.n]替代,只需要更 ...

  3. AWT06-事件处理

    在AWT中,用户的所有操作都要由事件处理来完成.Frame和组件本身没有处理事件的能力. 1.GUI事件处理机制 定义:在某个组件上发生某种操作时,自动触发某段代码. 事件处理涉及4个重要概念: 事件 ...

  4. 常见的 emit 实现 AOP demo

    0. 前言 上接:思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式 作为第四篇,我们回顾一下 csharp 里面比较常见动态编织实现方式emit 内容安排如下: emit a ...

  5. react第九单元(propTypes验证)

    第九单元(propTypes验证) #课程目标 理解类型验证的必要性 灵活掌握类型验证的使用 #知识点 在给react组件传属性的的时候,我们可以定义属性的类型,此时我们需要下载prop-types这 ...

  6. 如何push一个docker镜像到DockerHub上

    在DockerHub上创建账号:https://hub.docker.com/ 这里我的账号是firewarm 本地下载镜像(这里拿alpine做示例),并为镜像打tag [root@host-30 ...

  7. Vue - 与后端交互

    零:与后端交互 - ajax 版本1 - 出现了跨域问题 前端:index.html <!DOCTYPE html> <html lang="en"> &l ...

  8. 【基础】Linux系统的运行级别

    1.系统运行级别的配置文件 什么是运行级呢?简单的说,运行级就是操作系统当前正在运行的功能级别. 它让一些程序在一个级别启动,而另外一个级别的时候不启动. Linux系统的有效登录模式有0~9共十种, ...

  9. 3.自定义view-TextView变色

    1.效果 2.实现原理 自定义Textview,重写onDraw方法,将画布分成两部分,用不同颜色的画笔画 核心代码: @Override protected void onDraw(Canvas c ...

  10. DRF视图的使用及源码流程分析

    django rest framework中对于APIView.GenericAPIView.ModelViewSet.mixins扩展类的分析. APIView 示例 根据实际程序来分析: urls ...