文章目录

  • 原方案
    • 代码
    • 基本思路
      • 串口通信基本原理
      • 采集数据流程
    • 问题
  • 重构
    • 问题复盘
    • 重构过程
      • ReadTo函数
      • 多线程
      • 代码

原方案

欢迎大家访问我的个人网站 www.joezhouman.com 查看原文
首先说明一下,我对串口通信的理解不深,所有关于这方面的说明会有很多纰漏,有时会词不达意,各位图一乐就好。

代码

private char _start; // 起始位标志
private char _end;   // 结束位标志
private string recvstr;         // 用于存储一组数据的全局变量public void sendMsg(){Thread thread;thread = new Thread(() =>//新开线程,执行接收数据操作{while (enablescan)//如果标识为true{Thread.Sleep(1);try{serialPort1.WriteLine(":READ?");Thread.Sleep(AppCfg.devicepara.Scan_interval);}catch (Exception ex){;}}});thread.Start();//启动线程thread.IsBackground = true;
}private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e){string str = serialPort1.ReadExisting();if (str.IndexOf(_start) != -1)//当前数据存在起始位{str = str.Substring(str.IndexOf(_start), str.Length - str.IndexOf(_start);//截取起始位到字符串末尾}if(str.IndexOf(_end==-1)//当前数据不存在结束位recvstr += str;else{//当前数据存在起始位str = str.Substring(0, str.IndexOf(_end);recvstr = recvstr + str;DoSomething(recvstr)//对一组串口数据进行其他处理recvstr = "";//清空数据}}

基本思路

串口通信基本原理

说先说明一下采集仪发送数据的方式。一组数据一般由一个起始位标识开始,由一个结束位标识结束,中间为实际数据字符。
我们以S表示起始位,E表示结束位,D表示有效数据,那么一组数据是这样的

SDDDDDDDDDDDDDDE

而且,一组数据一般不是一次完整发送过来的,而是分段发送, 如:

SDDD
DDDD
DDDD
DDDE

发送过来的数据会放在串口通信的缓存区里。

采集数据流程

  1. 开始采集, enablescanfalse 变为 true
  2. 开启新线程,若检测到enablescantrue,则每个一段时间向采集仪发送采集信号
  3. 采集仪向串口发送数据,将数据放入缓冲区
  4. 串口接收到数据后触发serialPort_DataRecived事件,取走当前缓冲区的所有数据,进行处理(详细处理的方式见代码)
  5. 数据处理完后,退出serialPort_DataRecived函数,等待事件的下一次触发
  6. 停止采集,enablescantrue 变为 false
  7. 发送数据的线程检测到enablescanfalse,结束工作,销毁线程。

问题

在实际使用中,接受到的数据经常会缺失一部分,DoSomething时经常会抛出异常,造成了很大的困扰。

最开始的思路是用try...catch捕获异常,丢掉有问题的数据,但有时不赶巧,一组正常的数据都没有,而且这一问题出现的频率不低。
痛定思痛,决定重构这一祖传代码,真是前人拉屎总不冲,后人难忍终清洁

重构

问题复盘

我们首先复盘一下问题发生的过程
正常的过程应该是这样的

采集仪动作 接收事件 当前缓存区 str recvstr
开始 待命中 null null null
发送数据 触发事件 SDDD SDDD SDDD
发送数据 -处理中- DDDD SDDD SDDD
-发送间隔- 处理完成 DDDD SDDD SDDD
发送数据 触发事件 DDDDDDDE DDDDDDDE SDDDDDDDDDDE
开始休息 处理完成 null null null

产生错误时:

采集仪动作 接收事件 当前缓存区 str recvstr
开始 待命中 null null null
发送数据 触发事件 SDDD SDDD SDDD
发送数据 -处理中- DDDD SDDD SDDD
发送数据 -处理中- DDDDDDDE SDDD SDDD
-待机- 处理完成 DDDDDDDE SDDD SDDD
-待机- -待命中- DDDDDDDE SDDD SDDD
发送数据 触发事件 DDDDDDDESDDD SDDD SDDDSDDD

看,原本应该接收到SDDDDDDDDDDESDDD...,现在真正这组收到的是SDDDSDDD...,部分数据丢失。

对源代码进行修改太麻烦,故而选择直接重构该部分功能。

重构过程

ReadTo函数

首先想到的是,难到不能读到一组完整的数据再进行处理吗?

其实是可以的。C#内置了ReadTo方法 查看MSDN

一直读到结束位标识后,再这一组数据放到指定的参数中,底层肯定有一些细节,总而言之,可以保证每次读到一组完整的数据。

其用法为

    string endStr = ((char)13).ToStringstring targetStr = SerialPort.ReadTo("endStr");

多线程

ReadTo解决了数据读取的问题,然而当该方法在读取数据时,整个一直在等待,等读取成功后,再执行之后的操作,在这个等待过程中,
其他任何的触发事件都无法完成,呈现出的效果就是软件“卡住了”。

未解决这一问题,我们可以采用多线程的方法。一个线程专门负责读取数据,一个线程处理数据。
为了几个线程之间的通信,我们设计一个
读到的数据不断放入中,另一个线程不断从中提取数据。除此之外,我们还要保证先放入池中的数据先被处理。

根据以上特点,我们自然而然的想到了一种先入先出的数据结构–队列


说句题外话,老有人说小学时的问题一个池子,一边注水,一边放水这种问题很傻,没有现实意义。。。其实现实中处处都有。
不一定是真实的,也可以是抽象的


话不多说,上代码。

代码

private char _start; // 起始位标志
private char _end ;   // 结束位标志
private private Queue<string> _serialPortData; // 数据池///<summary>
///向测试仪发送读取信号及接受测试仪发回数据的线程。根据之前所说ReadTo的性质,正好保证读完一组数据后,再发送指令让测试仪测试下一组数据
///<summary>
public void ReadDataThread() {_enableScan = true;Thread thread = new Thread(() => //新开线程,执行接收数据操作{while (_enableScan) //如果标识为true{Thread.Sleep(1);try {serialPort1.WriteLine(":READ?");Thread.Sleep(10);if (serialPort1.BytesToRead != 0) _serialPortData.Enqueue(serialPort1.ReadTo(_end));//将读到的数据放入池中Thread.Sleep(100);}catch (Exception ex) {//其他的改进HandleException(ex);//加上异常处理,是系统更稳健Log.Error(ex);//打logger,记录异常原因,为修改bug提供依据}}});thread.Start(); //启动线程thread.IsBackground = true;//线程在后台运行
}public void SolveDataThread(){Thread thread = new Thread(() => //新开线程,执行接收数据操作{while (_enableScan) //如果标识为true{if (_serialPortData.Count == 0)continue; try{string str = _serialPortData.Dequeue();//从池中读取数据DoSomething(str);thread.Sleep(100);}catch (Exception ex) {//其他的改进HandleException(ex);//加上异常处理,是系统更稳健Log.Error(ex);//打logger,记录异常原因,为修改bug提供依据}}
}

信息说明都在代码注释里。

C#串口数据读取及处理解决方案--祖传代码修改记相关推荐

  1. python 串口读取地磅仪表_地磅串口数据读取解决办法

    地磅串口数据读取HT9800地磅通讯协议:串行通讯方式:连续发送称重值波特率:1200/2400/4800/9600可选数据格式:10位:1位起始位(0),8位数据位(D0-D7),1位结束位(1). ...

  2. 面向祖传代码 Debug,我挽回了一位准备跑路的程序员

    交流群的风格突然骤变,没有了往日的灌水扯淡,居然聊起了技术. 看了大家的全部的聊天记录,发现问题并没解决.群里难得这么多人聊技术,抱着问答不断,必有回响的原则,主动勾搭一起看看是什么问题. 大概了解其 ...

  3. 祖传代码如何优化性能?

    Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记! 为了新朋友能快速进入场景,再描述一遍这个项目的背景,这个项目是一个自研的Dubbo注册中心,上一张架构图 Consumer 和 Pr ...

  4. 基于小波变换的图像边缘检测(matlab祖传代码注释)

    基于小波变换的图像边缘提取应用展示 上图为针对png格式无背景原图的边缘检测,对比各种边缘检测算子,小波变化的优势体现并不明显. 上图为针对含背景图片的边缘检测,小波变化的优势这里体现的比较明显. m ...

  5. 为什么祖传代码被称为「屎山」?这个回答简直太形象了

    经常听说祖传代码会被人称之为「屎山」,不同人可能有不同的体会,最近看到一个回答,简直是把这个阐述得"活灵活现",大家来感受下吧. " 阅读本文大概需要 3 分钟. &qu ...

  6. 入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???

    公司各种各样的祖传代码都是令新人虎躯一震的代码,因为有时候你根本不知道它是干嘛的,甚至觉得它毫无用处,关键是 还绝对不能动,碰一段改半年,别问我怎么知道的.最讽刺的是,你可能为了修改代码,也在里面拉了 ...

  7. 为什么祖传代码被称为“屎山”?

    任何设计人员,你几年之后再来看自己现在的作品,你就会觉得简直就是狗屎,拿出来真tm丢人. 如果你没有这种感觉,那说明你这行干不久了. 说到祖传代码不得不提之前在知乎上看到的两位网友的经历: 一 我刚入 ...

  8. 祖传代码成「屎山」了,千万别动

    上面这个公号「涩郎」,是我的一个备用号,为了防止万一哪天大号失联,平时一周我也会发三篇左右的我的思考,读书笔记,认知感悟等文章,带领大家一起探索精神与财务自由之路. 大家好,我是校长. 我看有人问了这 ...

  9. read函数读串口数据“分包”问题及解决方案

    最近在做一个项目,其中要实现一个简单的数据透传功能. 功能简介: 实现通过网络向485串口(温湿度传感器)发送指令,然后把串口返回的数据发送到网络端. 实现方法: 采用多线程技术,函数执行时创建两个线 ...

最新文章

  1. 悲催的跨平台文献管理能力
  2. 『科学计算』可视化二元正态分布3D科学可视化实战
  3. form 表单 + HTML5(FileReader) +iframe 实现无刷新图片上传+图片预览效果
  4. js获取本周、本月、本季、本年的第一天
  5. Rust 1.27支持SIMD
  6. Hadoop学习之MapReduce(二)
  7. sqlserver2000内存突破4g_酷比魔方iPlay30评测:10.5英寸大屏,支持4G全网通
  8. 企业日志分析ELK(Logstash+Elasticsearch+Kibana)介绍及搭建
  9. CAGradientLayer简介 实现颜色渐变
  10. LeetCode 445. Add Two Numbers II
  11. 网络信息安全常用术语
  12. 在VM14中建立Win10虚拟机并实现与宿主机的互联
  13. 20200427 FTL 模板语言参考 梳理 (常用)
  14. AS中NDK环境搭建
  15. 2022年中国中小学教育信息化行业研究报告
  16. P3840蜗牛一期--虚拟局域网VLAN
  17. java 转byte字符串,Java 字符串与byte之间的相互转换
  18. webview加载html页面闪烁,webview加载Html页面
  19. JLINK识别不到芯片
  20. SSD硬盘在检测中出现数据损坏的处理

热门文章

  1. C++中static成员
  2. java-php-python-ssm计算机office课程平台计算机毕业设计
  3. Appium-Maximize Window(最大化窗口)
  4. android有6G吗,6G RAM安卓来了 你还买2G RAM iPhone吗
  5. 【企业】把握蘑菇管理原理,合理培养人才
  6. Cordova电池状态插件使用
  7. 1311. Get Watched Videos by Your Friends**
  8. arm-linux-gcc使用教程,arm-linux-gcc安装使用教程
  9. 02-分享:个人利用 Python 爬虫技术怎么挣钱-1万被动收入
  10. 花落的伤感日志分享:你想要的爱,我给不起