C#串口数据读取及处理解决方案--祖传代码修改记
文章目录
- 原方案
- 代码
- 基本思路
- 串口通信基本原理
- 采集数据流程
- 问题
- 重构
- 问题复盘
- 重构过程
- 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
发送过来的数据会放在串口通信的缓存区
里。
采集数据流程
- 开始采集,
enablescan
由false
变为true
- 开启新线程,若检测到
enablescan
为true
,则每个一段时间向采集仪发送采集信号 - 采集仪向串口发送数据,将数据放入缓冲区
- 串口接收到数据后触发
serialPort_DataRecived
事件,取走当前缓冲区的所有数据,进行处理(详细处理的方式见代码) - 数据处理完后,退出
serialPort_DataRecived
函数,等待事件的下一次触发 - 停止采集,
enablescan
由true
变为false
- 发送数据的线程检测到
enablescan
为false
,结束工作,销毁线程。
问题
在实际使用中,接受到的数据经常会缺失一部分,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#串口数据读取及处理解决方案--祖传代码修改记相关推荐
- python 串口读取地磅仪表_地磅串口数据读取解决办法
地磅串口数据读取HT9800地磅通讯协议:串行通讯方式:连续发送称重值波特率:1200/2400/4800/9600可选数据格式:10位:1位起始位(0),8位数据位(D0-D7),1位结束位(1). ...
- 面向祖传代码 Debug,我挽回了一位准备跑路的程序员
交流群的风格突然骤变,没有了往日的灌水扯淡,居然聊起了技术. 看了大家的全部的聊天记录,发现问题并没解决.群里难得这么多人聊技术,抱着问答不断,必有回响的原则,主动勾搭一起看看是什么问题. 大概了解其 ...
- 祖传代码如何优化性能?
Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记! 为了新朋友能快速进入场景,再描述一遍这个项目的背景,这个项目是一个自研的Dubbo注册中心,上一张架构图 Consumer 和 Pr ...
- 基于小波变换的图像边缘检测(matlab祖传代码注释)
基于小波变换的图像边缘提取应用展示 上图为针对png格式无背景原图的边缘检测,对比各种边缘检测算子,小波变化的优势体现并不明显. 上图为针对含背景图片的边缘检测,小波变化的优势这里体现的比较明显. m ...
- 为什么祖传代码被称为「屎山」?这个回答简直太形象了
经常听说祖传代码会被人称之为「屎山」,不同人可能有不同的体会,最近看到一个回答,简直是把这个阐述得"活灵活现",大家来感受下吧. " 阅读本文大概需要 3 分钟. &qu ...
- 入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???
公司各种各样的祖传代码都是令新人虎躯一震的代码,因为有时候你根本不知道它是干嘛的,甚至觉得它毫无用处,关键是 还绝对不能动,碰一段改半年,别问我怎么知道的.最讽刺的是,你可能为了修改代码,也在里面拉了 ...
- 为什么祖传代码被称为“屎山”?
任何设计人员,你几年之后再来看自己现在的作品,你就会觉得简直就是狗屎,拿出来真tm丢人. 如果你没有这种感觉,那说明你这行干不久了. 说到祖传代码不得不提之前在知乎上看到的两位网友的经历: 一 我刚入 ...
- 祖传代码成「屎山」了,千万别动
上面这个公号「涩郎」,是我的一个备用号,为了防止万一哪天大号失联,平时一周我也会发三篇左右的我的思考,读书笔记,认知感悟等文章,带领大家一起探索精神与财务自由之路. 大家好,我是校长. 我看有人问了这 ...
- read函数读串口数据“分包”问题及解决方案
最近在做一个项目,其中要实现一个简单的数据透传功能. 功能简介: 实现通过网络向485串口(温湿度传感器)发送指令,然后把串口返回的数据发送到网络端. 实现方法: 采用多线程技术,函数执行时创建两个线 ...
最新文章
- 悲催的跨平台文献管理能力
- 『科学计算』可视化二元正态分布3D科学可视化实战
- form 表单 + HTML5(FileReader) +iframe 实现无刷新图片上传+图片预览效果
- js获取本周、本月、本季、本年的第一天
- Rust 1.27支持SIMD
- Hadoop学习之MapReduce(二)
- sqlserver2000内存突破4g_酷比魔方iPlay30评测:10.5英寸大屏,支持4G全网通
- 企业日志分析ELK(Logstash+Elasticsearch+Kibana)介绍及搭建
- CAGradientLayer简介 实现颜色渐变
- LeetCode 445. Add Two Numbers II
- 网络信息安全常用术语
- 在VM14中建立Win10虚拟机并实现与宿主机的互联
- 20200427 FTL 模板语言参考 梳理 (常用)
- AS中NDK环境搭建
- 2022年中国中小学教育信息化行业研究报告
- P3840蜗牛一期--虚拟局域网VLAN
- java 转byte字符串,Java 字符串与byte之间的相互转换
- webview加载html页面闪烁,webview加载Html页面
- JLINK识别不到芯片
- SSD硬盘在检测中出现数据损坏的处理
热门文章
- C++中static成员
- java-php-python-ssm计算机office课程平台计算机毕业设计
- Appium-Maximize Window(最大化窗口)
- android有6G吗,6G RAM安卓来了 你还买2G RAM iPhone吗
- 【企业】把握蘑菇管理原理,合理培养人才
- Cordova电池状态插件使用
- 1311. Get Watched Videos by Your Friends**
- arm-linux-gcc使用教程,arm-linux-gcc安装使用教程
- 02-分享:个人利用 Python 爬虫技术怎么挣钱-1万被动收入
- 花落的伤感日志分享:你想要的爱,我给不起