串口是很简单的,编写基于串口的程序也很容易。新手们除了要面对一堆的生僻概念,以及跨线程访问的细节,还有一个需要跨越的难题,就是协议解析,上一篇已经说明了:

一个二进制格式的协议一般包含: 协议头 + 数据段长度 + 数据  + 校验

一个Ascii格式的文本协议,一般包含: 数据头 + 正文 + 数据结束标识

类似的命令可能很多,类似的代码也会重复写很多次。对于我,并不觉得这个有任何难度,但是,很多时候,需要写点类似东西的时候呢,我往往不想写,不是别的,要搭建一个这样的框架,这绝对是个体力活,而且还需要耐心细心

从我上一次带项目,我就开始考虑编写通用的一个通讯库,支持很多功能,不过和公司内容结合紧密,不适合开源,更不适合推广。我重新组织、抽象了各个概念。希望能让新人朋友减少学习难度,更快的投入到其他方面。

请注意,此文章我也不知道如何归纳,不算科普,不能算类库的介绍,我是在介绍如何设计一个这样的通讯库。

通讯库,并非串口库,所以,我希望有一个基类,可以描述各种通讯方法的基类或接口,微软已经这么做了,他把这个叫做Stream。我认为不好的理由是,提供了Length属性、peek方法、seek方法却无法使用,很多方法和属性是不支持的,如果使用这个类操作硬件,就像一颗地雷,不小心就会写一个不支持的操作,而且会在运行时报错。所以,我希望能针对流设备的硬件,重新设计,我抽象出了一个接口:ICommunication。提供基本的打开、关闭、读写、字符集和有效数据长度等流设备的特性和操作。

为了能有一个通用的配置类,我定义了一个接口:ICommunicationSetting。

当你实现一个设备的时候,你需要实现ICommunication,还需要编写一个设置的类,去实现ICommunicationSetting接口。别觉得麻烦,这是为了能抽象的好,编写一个一劳永逸不用经常重写的通用代码。有了2个接口,我甚至可以开始编写依赖此接口的功能或软件了。当然,我还有需要写有关协议的分析。

既然协议是分2种,那自然要编写BinaryXXX和TextXXX,没错,有这样2个类。

考虑的更详细一点,任何数据,都不是无限期有效的,比如你获取下位机发来的电压,过了几秒了,应该就无效了,所以要考虑定时失效,于是我实现了有效性检查。数据要在字节数组中查找,分析,通知。所以这些公共的部分,我抽出来了,我写了一个接口,叫做:IAnalyzer,并编写了默认的实现,于是有了AnalyzeResult类,同时,区分2种协议方式,创建了子类:BinaryAnalyzeResult和TextAnalyzeResult。

那么,谁来使用ICommunication,IAnalyzer呢?放心,联系有点紧密,我不会撒手扔给外面的,这样做反而更复杂了,不是么。所以我写了一个带有分析功能的类:WyzComm。

使用通讯库的

这个类实现了数据的采集、缓存、分析器的调用,以及事件调用的通知。数据死锁的控制,所有你认为的麻烦事情,都在这里做了。那么,我编写这个类的时候,我肯定不知道未来有多少种协议是不是?那怎么办呢?我无法写死分析器,所以,我编写了接口:IAnalyzerCollection,因为文章从串口说起,我首先提供了串口的实现:

SerialPort(此类和微软的那个名字一样而已,但不是同一个),实现了ICommunication接口,我定义了一个SerialPortSetting类,实现了ICommunicationSetting。

至此。通讯库的框架就完成了。而这也就是使用通讯库所需要关注的所有内容。下面,为了能进行实际的演示,我编写了简单的实现。来演示一种功能,假设我有个程序,需要同时分析二进制数据格式和ASCII的文本数据格式,数据各不相同,使用了通讯库之后,我不需要重写数据的缓存、关闭的死锁处理、数据对界面的通知。我只需要编写2个协议类,和1个协议集合类。我的数据分析工作就完成了。

首先是一个文本协议,协议头是WYZ,协议尾是回车换行,中间是一个整形数字。我只需要设置好头、尾,编写数据分析。

[c-sharp] view plaincopyprint?
  1. public class MyData1 : TextAnalyzeResult<int>
  2. {
  3. public MyData1()
  4. {
  5. this.BeginOfLine = "WYZ";
  6. this.EndOfLine = "/r/n";
  7. }
  8. public override void Analyze()
  9. {
  10. string s = Encoding.GetString(Raw);
  11. Match m = Regex.Match(s, "//d+");
  12. if (m.Success)
  13. {
  14. this.Data = int.Parse(m.Value);
  15. this.Valid = true;
  16. }
  17. }
  18. }

public class MyData1 : TextAnalyzeResult<int> { public MyData1() { this.BeginOfLine = "WYZ"; this.EndOfLine = "/r/n"; } public override void Analyze() { string s = Encoding.GetString(Raw); Match m = Regex.Match(s, "//d+"); if (m.Success) { this.Data = int.Parse(m.Value); this.Valid = true; } } }

然后我定义了一个二进制协议,分析一条数据包含2个子项。

我首先定义这个数据的具体类型

[c-sharp] view plaincopyprint?
  1. public class SampleData
  2. {
  3. public int Version { get; set; }
  4. public float Voltage { get; set; }
  5. public SampleData()
  6. {
  7. Version = 0;
  8. Voltage = 0;
  9. }
  10. public override string ToString()
  11. {
  12. return string.Format("{0},{1}", Version.ToString(), Voltage.ToString());
  13. }
  14. }

public class SampleData { public int Version { get; set; } public float Voltage { get; set; } public SampleData() { Version = 0; Voltage = 0; } public override string ToString() { return string.Format("{0},{1}", Version.ToString(), Voltage.ToString()); } }

然后我编写协议分析类

[c-sharp] view plaincopyprint?
  1. public class MyData2 : BinaryAnalyzeResult<SampleData>
  2. {
  3. public MyData2()
  4. {
  5. this._mask = new byte[] { 0xAA, 0xBB, 0xCC };
  6. this.TimeOut = 5;//超过5秒,收不到数据,则此数据无效。
  7. //自定义校验方法,演示为逐个相加和随便一个数字取模,我选择的是42
  8. this.checksum = (buf, offset, count) =>
  9. {
  10. byte checksum = 0;
  11. for (int i = offset; i < offset + count; i++)
  12. {
  13. checksum = (byte)((checksum + buf[i]) % 42);
  14. }
  15. return checksum;
  16. };
  17. }
  18. public override void Analyze()
  19. {
  20. int offset = _mask.Length + LenLength;//_mask.Length表示标记后的一个字节,_mask.Length+1表示标记后的第二个字节,有一个字节表示长度。
  21. this.Data.Version = BitConverter.ToInt32(Raw, offset + 0);
  22. this.Data.Voltage = BitConverter.ToSingle(Raw, offset + 4);
  23. this.Valid = true;//注意要设置数据有效状态
  24. }
  25. }

public class MyData2 : BinaryAnalyzeResult<SampleData> { public MyData2() { this._mask = new byte[] { 0xAA, 0xBB, 0xCC }; this.TimeOut = 5;//超过5秒,收不到数据,则此数据无效。 //自定义校验方法,演示为逐个相加和随便一个数字取模,我选择的是42 this.checksum = (buf, offset, count) => { byte checksum = 0; for (int i = offset; i < offset + count; i++) { checksum = (byte)((checksum + buf[i]) % 42); } return checksum; }; } public override void Analyze() { int offset = _mask.Length + LenLength;//_mask.Length表示标记后的一个字节,_mask.Length+1表示标记后的第二个字节,有一个字节表示长度。 this.Data.Version = BitConverter.ToInt32(Raw, offset + 0); this.Data.Voltage = BitConverter.ToSingle(Raw, offset + 4); this.Valid = true;//注意要设置数据有效状态 } }

完成了。一个基于串口的,同时分析2种数据的,数据具有有效性判断,支持独立数据通知界面,整体原始数据缓存显示的功能。完成了。

为了演示功能,我写了新的校验方式,当然,你不用管,默认已经支持了异或校验,后续还会把常用校验都添加进去,crc16,crc32,奇偶校验等。

模拟发送数据为:

文本格式发送:WYZ123<CR><LF>

二进制格式发送:AA BB CC 08 0A 00 00 00 FA 3E F7 42 05

ps:转自http://blog.csdn.net/wuyazhe/article/details/5797673

C# 串口操作系列(5)--通讯库雏形相关推荐

  1. C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子。

    我假设读者已经了解了c#的语法,本文是针对刚打算解除串口编程的朋友阅读的,作为串口编程的入门范例,也是我这个系列的基础. 我们的开发环境假定为vs2005(虽然我在用vs2010,但避免有些网友用20 ...

  2. C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wuyazhe/archive/2010/05/17/5598945.aspx 我假设读者已经了解了c#的语法,本文是针 ...

  3. C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子。 ——兔子党逍遥原创,转来分享学习

    我假设读者已经了解了c#的语法,本文是针对刚打算解除串口编程的朋友阅读的,作为串口编程的入门范例,也是我这个系列的基础. 我们的开发环境假定为vs2005(虽然我在用vs2010,但避免有些网友用20 ...

  4. C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wuyazhe/archive/2010/05/27/5627253.aspx 我们的串口程序,除了通用的,进行串口监听 ...

  5. C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ?

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wuyazhe/archive/2010/05/19/5606276.aspx 第一篇文章我相信很多人不看都能做的出来, ...

  6. C# 串口操作系列(4) -- 协议篇,文本协议数据解析 .

    上一篇已经介绍了协议的组成,一个协议,一般具有 :协议头+长度+数据+校验 , 文本格式可以直观的定义回车换行是协议的结尾,所以我们可以省略数据长度,增加协议尾.即: 协议头 + 数据 + 校验 + ...

  7. C# 串口操作系列(4) -- 协议篇,文本协议数据解析

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wuyazhe/archive/2010/06/09/5657188.aspx 上一篇已经介绍了协议的组成,一个协议,一 ...

  8. python 串口_Python串口操作库pyserial(1)

    测试过程中需要用到串口,目前采用pyserial库. 1 采用命令行安装pyserial库: pip install pyserial 2 获取可用的串口号,见Listing available co ...

  9. C# For Q Series Ethernet Communication Library,C#与三菱Q系列PLC以太网通讯库

    C# For MITSUBISHI Q Series PLC Ethernet Communication Library C# 与 三菱 Q 系列PLC以太网通讯库 此通讯库使用简单,支持bool, ...

最新文章

  1. c语言已知先序还原二叉树,(c++ 递归)先序 中序 还原二叉树
  2. 教你如何配置Tomcat 绑定指定目录的工程文件
  3. 电脑装服务器系统好处,服务器选用Linux系统的几个好处
  4. 卸载mongodb_【数据库】mongodb数据库安装
  5. no segments* file found in org.apache.lucene.store.SimpleFSDirectory
  6. linux下查看cpu信息
  7. 解决Pycharm添加虚拟解释器的报错问题
  8. 为什么用dict.get(key)而不是dict [key]?
  9. 谷歌浏览器不能用flash插件的问题
  10. FLASH寿命问题说明
  11. 2018年湖南省高中数学联赛(A)卷试题
  12. 阵列信号处理笔记-波达方向DOA-子空间方法
  13. 诗词乱拼 zz from smth.org
  14. 征信报告 加密文档_如何给PDF文档加密?PDF文档加密的方法
  15. 4384---一方通行与最后之作 (sdut oj)
  16. Linux开放MySql 3306端口
  17. Windows无法安装到GPT格式磁盘的根本解决办法 - 初学者系列 - 学习者系列文章...
  18. oracle甲骨文账号-用于下载oracle JDK
  19. 网络知识:快速了解IP地址的概念以及IPV4和IPV6的区别!
  20. 用于威胁情报分析的虚拟机

热门文章

  1. Java的位运算符——与()、非(~)、或(|)、异或(^)
  2. 深入分析Java ClassLoader原理
  3. Cocos Studio 2.3.2不再支持直接导入PSD文件
  4. 基于HAProxy的网站架构
  5. 基本配置4-被忽悠进了CentOS 6
  6. 微软、谷歌、百度等公司经典面试100题[第1-60题]——自己的实现[转]
  7. 关于研究网站开发还是应用程序的思考
  8. Vista初级使用技巧及故障总结
  9. display:none的表单也会被提交
  10. 详解SESSION与COOKIE的区别