[连载]《C#通讯(串口和网络)框架的设计与实现》- 0.前言
目 录
第一章 通讯框架介绍... 2
1.1 通讯的本质... 2
1.2 框架简介... 3
1.3 解决现实问题... 4
1.4 应用场景... 5
1.5 框架应用特点... 6
1.6 框架设计特点... 7
1.7 插件式应用框架... 9
1.8 开发环境... 10
1.9 第三方组件... 11
1.10 小结... 12
第一章 通讯框架介绍
1.1 通讯的本质
通讯就是信息的传递,信息传递又分为:单向信息传递和双向信息传递。用喇叭进行广播是单向信息传递,打电话是双向信息传递。
单向信息传递相对较为简单,只需要向信息接收者实时发送数据,而不用管信息是否到达,以及到达后是否进行了处理。这种信息传递方式适用于对数据完整性要求不高的应用场景,例如:采集温度传感器的数据。但是,如果数据源或是传感器比较多的话,要考虑到并发量的问题,随着互联网技术的发展,并发问题是可以很好的解决。
双向信息传递相对较为复杂,不仅涉及到发送数据的问题,还涉及到信息握手、数据补传等一系列交互问题。如果把双向信息传递非要分成客户端和服务端的话,还涉及到是哪一方先发起信息传递,客户端主动向服务端发送数据,服务端接收到数据后进行处理;但是,有时候服务端不希望接收到客户端的数据,只有在服务端向客户端发送请求命令后,客户端根据命令才可以返回相应的数据。在与硬件进行双向通讯的时候,还涉及到载波通道是半双工和全双工的问题,半双工是同一时刻在通道上只能A向B或B向A发送数据,只能单向数据传输;全双工是A向B发送数据,同时B向A也可以发送数据,发送和接收数据两者可以同步进行。这种信息传递方式适用于对数据完全性要求比较高的应用场景。
不管是单向信息传递,还是双向信息传递,都涉及传输协议、编码方式和数据校验。传输协议是能够封装和解析并且能够相互理解的数据格式,它是一种数据规约方式,可以使用标准的协议方式,例如:Modbus、XMPP、AMQP、MQTT等,也可以使用自定义协议;有了传输协议后,在传输过程中还涉及到编码方式,例如:GBK、UTF、ASCII,有可能在编码的基础上还要进行加密,以保证数据的安全性;为了数据包完全性、可解析性,还要增加对数据的校验,一般采用较多的校验方式为CRC。传输协议、编码方式和数据校验的目的只有一个:防止数据在传输过程中受到干扰,或被恶意篡改,给数据处理造成意想不到的后果。打个比喻,一个中国人说普通话,一个外国人说美式英文,语法不一样,编码格式不一样,结果造成说话听不懂、文字看不懂,如果误认为是在骂人,有可能还要打一架。
现在基本都是面向对象开发方式,new出来一个对象,把对象的属性赋值后,直接把对象传给接口函数完成发送数据。这种操作方式使开发者更多的关注业务层面,从而掩盖了很多技术细节,例如:序列化、协议、编码、字节流的操作等等。
但是,SuperIO保持对底层字节流(byte[])的操作,更多的关注通讯框架、数据协议、数据缓存、数据处理流程、设备驱动、插件、二次开发等方面。因为在物联网时代,将会面对很多数据源,包括:各种传感器、手机、PC端、智能硬件、传统嵌入式设备等等,协议众多,并且很难统一,所以最直接的操作数据就是字节流(byte[])。另外,很早以前传输技术不发达(300波特率),同时受寄存器的存储限制,为了减小数据量,1个字节的8位要表示8种状态类型。
在物联网时代,将面临各种通讯情况,例如:一个串口通道,一对一、一对多的方式通讯;一个网络IP通道,一对一、一对多的通讯。所以,没有一个好的框架支撑是无法满足通用性的要求。
有人问题串口通讯、网络通讯怎么做,有人回答这些很容易,但是要把上述问题以及其他问题都考虑周全的话就是一个复杂的问题,并且有些问题不是很好解决。
1.2 框架简介
如果一个公司的硬件产品众多,协议又各不相同,每一个硬件产品都对应一套上位机软件,需要专人维护。而客户的需求日益变化,造成维护成本较高,并且阻碍了公司的快速发展。另外,就算修改同类硬件产品的配套软件,也可能造成新的BUG出现。
随着市场和公司发展的需要,需要整合、重构软件系统以适应环境、硬件的不断变化,降低人力、运维成本,释放劳动力。
所以,对于发展到一定阶段、或是一个成熟的公司必然要有软件框架作为支撑,这是从业务角度考虑发展应用框架的必然性。
技术方面,框架是一个系统全部或部分的可复用设计,通常由一组接口、抽象类和类之间的协作组成。随着信息化的发展,软件产品的开发也越来越复杂化,解决问题的复杂度也在不断的提高。IT界也在寻找多种方法,包括制定各种软件开发标准和规范、开发更高级更有生产力的编程语言、开发更好的编译器和运行时以及不需要编译的解释性开发语言、开发功能强大以及更通用性的组件库、探索适用不同应用场景的设计模式等。
从软件工程角度出发,在设计层面要采用独特的软件构架和设计模式来达到我们预期的目标:
- n 尽量提高软件的可重用性,避免不必要的重复编码工作。
- n 增加组装的封装性。
- n 提高软件的模块化程度。
- n 不同功能模块之间能够无缝集成。
- n 软件具有灵活的可扩展性。
- n 软件产品的扩展和开发实现标准化。
- n 软件产品具有面向不同应用层面的适应性和易移植性。
为了实现这些要求,在设计层面上,越来越多的软件产品开始采用应用框架的思想进行软件结构设计。应用框架已经是一个被广泛使用的术语,它成为软件开中一种非常实用并且常用的设计、开发规范。
我们肯定见过很多自称“框架”的软件产品,也许有人会感觉不屑,有些代码量很少的程序居然也称自己是某种形式的应用框架?事实上,应用框架无关乎规模大小,就像房屋一样,摩天大楼和民房都是房屋,只不过它们的规模和精巧度大小不一样而已。
在架构师眼里,代码都是需要设计的,都是有框架的。
1.3 解决现实问题
在工业领域,经常遇到软硬件之间的数据交互,并且面临着复杂的现场环境:
(1)复杂的、多样的通讯协议。有标准的协议,例如:Modbus等,也有很多根据标准协议修改的协议格式、以及自定义协议格式,并且千差万别。对于不好的软件架构,疲于应对,增加设备或协议要对整个软件进行梳理,往往在此过程中出现新的问题或BUG。
(2)针对不同用户对软件界面或功能的要求有很大不同,使之满足不同用户的显示要求,可以自定义数据显示界面。
(3)在做集成项目的时候,输入输出数据的多样性。首先,要集成其他厂家的设备,要求数据进行接入。其次,还有很多是其他厂家要集成自己家的设备,就涉及的输出数据的问题,数据格式要求也是千差万别。
(4)通讯链路的多种性,对于同一个设备可能要支持RS232/RS485/RS422、RJ45、3G/4G等通讯方式,所以对于一个设备要对应多种通讯方式(串口和网络),也给我们的开发造成很大的障碍。
(5)软件各版本、以及软件与硬件之间的兼容性很差,管理起来错综复杂。
为了解决以上诸多问题,开发一个软件框架,支持二次开发。在不对软件框架改动的情况下,能够很方便的接入设备、维护设备、集成设备、处理设备业务数据等。软件框架相对稳定,把容易变化的部分进行灵活设计。
1.4 应用场景
作为一个框架平台,在形成产品后要定位它的应用场景,在设计框架之前要有清晰的认识,并在设计过程中不断强化应用目标。
在产品应用方面,框架平台可能要部署在PC机上,与众多硬件、传感器进行数据交互,并在本地进行数据存储。
在项目应用方面,框架平台可能部署在服务器端,与客户端(PC机、硬件、传感器等)进行数据交互,并存储到数据中。
既然框架平台在PC机上和服务端都可能应用,那么框架与框架之间也有数据交互的可能性。
所以,框架平台的交互场景包括两方面:第一、与硬件产品交互。第二、与软件产品交互。基本这两方面考虑:
1)框架平台应用在PC机上
主要应用在自动站的工控机上,通过RS485/RS232、RJ45、4-20mA等方式
采集硬件设备的数据信息。同时,通讯平台与服务器端的软件进行交互,负责上传数据信息,以及接收控制命令等。
2)框架平台应用在服务器端上
终端设备以3G/4G、有线专网、卫星等与通讯平台连接,进行数据交互,终
端设备包括:PC机、移动终端(手机)、监测设备和传感器等。
基于以上考虑,框架平台的应用场景结构图如下:
1.5 框架应用特点
对于框架的特点,我们要有简单、清晰的规划,其中包括:功能层面、性能层面、应用层面、运行层面、二次开发层面等等 ,这些将强化我们在设计、开发过程的目标。这些不仅要写在纸上,更要记在脑子里。SuperIO在设计的时候,简单的列出了它的特点,尽管有些特点是后来完善的,如下:
- n 快速构建通讯数据采集平台软件的宿主程序
- n 快速构建设备驱动,以及相关的协议驱动、命令缓冲、自定义参数和实时数据属性等
- n 快速二次开发图形显示、数据输出、服务驱动,并以插件的形式进行挂载。
- n 一个设备驱动,同时支持串口(COM)和网络(TCP Server/Tcp Client)通讯机制,可以*切换
- n 内置协议驱动,可以把第三方协议转换成自定义的协议,协议的本质是对字节流的操作。
- n 内置设备命令缓冲器,可以设置命令发送的优先级别,保证命令的快速响应。
- n 以服务驱动插件的方式对OPC服务、4-20mA输出、LED大屏显示、短信服务等进行二次开发。
- n 快速开发、运行稳定、扩展性强大
- n 适用工业上位机软件,以及系统集成中采集远程设备数据
- n 支持Windows XP/7/8/8.1、Windows Server 2003/2008/2012
1.6 框架设计特点
有些书籍说了一大堆设计特点,有点让人不知所云,没见有层次感,我认为对于此类框架的特点最重要的包括两点:稳定性、扩展性、性能。
稳定性
对于一个实时数据采集框架来说,首要的设计特点就是稳定性,这是其他一切特点的前提。不能出现异常后软件无故退出的现象、不能出现关闭软件后进程无法退出的现象、不能出现无法响应数据的现象、不能出现无法处理数据的现象等等。
基于可能存在的这些潜在的问题,我们要考虑:容错机制、模块无缝对接、记录日志等。
容错机制是所有软件都有的一种机制,核心思想是对异常状态的处理方法。对于操作一般性的功能,如果出现异常状态,我们可能不需要过多的干预,只需要进行日志记录就可以了,对于再次操作同样的功能可以验证异常状态的可重复性,根据日志信息可以有针对性的进行解决;对于事务性的任务,对异常状态的处理会有多种选择,可以简单的记录异常信息、可以销毁当前的资源,重新开始任务,直接任务成功、可以恢复到出现异常状态的节点等,根据不同的场景,选择处理的方式也不一样。就相当于,某人说错话了,要进行补救,那就要看当时的环境和面对的人,如果是好朋友,这事就算过去了。
模块无缝对接要求我们对接口、抽象类以及类的模块划分、设计粒度有很好的把握,更多的体现在经验方面。模块之间是一个契约关系,如何履行契约会涉及到很多设计模式的选择,所以说对设计模块的把握程度直接影响软件框架的成熟度。就好比两个人对话,说话方式、语意都不能相互理解,就有可能话不投机半句多。
记录日志是所有软件必须要有的特点,这为我们排查错误提供了很大的方便。日志记录有很多开源的项目可以拿来直接使用,例如常用的Log4Net。但是,有时间研究这东西的时间,自己也能写一个适用于自己的日志库了。
稳定性是软件运行的最直接反应,是所有实时性框架设计最主要考虑的因素,也是最难达到的。
扩展性
用户可能比设计者更关心稳定性,但是用户不仅仅满足于稳定性,还会提出各种新需求,更多的体现在功能方面。如果扩展性不好,对于开发者来说是万丈深渊。
所以,可扩展性是应用框架最显著的特征之一,它意味着应用框架的功能具有生长能力。没有扩展能力的应用框架毫无使用价值和意义,因为框架本身就是为了提供一个统一的上下文环境给具体的应用使用。应用框架的可扩展性使我们能够基于一个平台实现不同的功能,满足不同的应用需求,有些需求是框架本身就支持的。
框架的可扩展性主要是通过继承和聚合两种方式实现的。继承方式是指通过派生类继承基类或接口,通过重用基类的功能并定义新的功能的方式实现功能扩展;聚合方式是指调用不同的类型组合为一个新类型而扩展出全新的功能。研究Framework框架源代码,能够深切感受到继承和聚合的作用。
如果单说扩展性会让人有些空洞,那么我们还要考虑模块化、可重用性、可维护性等等。
模块化,并不是把每个功能都编译成一个DLL程序集就可以称之为模块化,一个程序集内部也可以模块化。从框架层面在逻辑上横向、纵向对模块和层次进行划分,以降低模块之间的耦合度,不会因为一个模块的变化而影响其他模块,划分模块时保证模块之间输入输出的统一性。
可重用性,也可以称为可复用性,是衡量代码质量的重要标志之一。既然是框架设计其中一个目的就是提高效率,减少没有必要的重复劳作,降低成本。一般来说,框架可重用可以是离散存在的函数、可以是封装好的类库、可以是封装好的众多类库,以方便我们在类似功能、业务中使用。
可维护性,根据业务需求变化能够方便进行改变的能力,也是扩展性的落脚点。保证我们尽量少修改代码完成需求而又不影响软件的整体运行。
性能
性能是软件运行效率的重要指标,是对软件运行极限的考验。例如,不管挂载多少设备驱动,用户要求1秒钟要读取一次所有设备的数据,如果实现不了,用户说对不起,我们不能签合同。
在互联网行业对性能的要求更高、更全面,有很多指标性的参数,例如:响应时间、延迟时间、吞吐量、并发量、资源利用率等等,所以一般要对软件、服务进行压力测试。在传统行业方面也不防借鉴使用先进的框架或第三方组件,例如:消息队列框架(kafka、ActiveMq、RabbitMq、ZeroMq、EQueue),响应式消息框架(Akka.net)、作业调度框架(Quartz.net)等等,这些能够有助于提高软件、系统的执行效率和性能。
当然,对于性能来讲,软件只是一个方面,更多的还涉及到网络结构、服务器部署等方面,是一项综合性的结构。
对于稳定性、扩展性、性能,它是一个整体的三个方面。相信大家都看过F1比赛,要求赛车在高速行驶过程中保持不翻车,高速行驶对轮胎磨损很严重,并且要求在很短的时间内方便对轮胎的更换。
1.7 插件式应用框架
插件技术是在软件的设计和开发过程中,将整个应用程序划分为宿主程序和插件对象两部分,宿主程序能够调用插件对象,插件对象能够在宿主程序上实现自己的逻辑,而两者的交互基于一种公共的通信契约。宿主程序可以独立于插件对象存在,即使没有任何插件对象,宿主程序的运行也不受影响,因此,我们可以在避免改变宿主程序的情况下通过增减插件或修改插件的方式增加或调整功能。由于使用了插件技术的宿主程序具备了一个框架的本质特征,因此可以将它看作是一种插件式框架。插件式框架能够有效地降低功能对象与对象管理逻辑之间的耦合程度,并将耦合置于最优的程度。
对大部分计算机用户和软件开发者而言,插件式应用框架其实算不上什么神秘的东西,事实上,几乎每个人都曾使用过具有插件式功能的软件产品。这些软件有大有小,从操作简单的诸如播放器软件到复杂桀骜的各种专业应用软件,都或多或少采用过插件机制,只是对于最终用户而言,由于常常满足于使用一款成熟软件,很少有人刻意去关注这些软件使用的是什么样的架构体系。
Visual Studio IDE、Elipse等都是插件式的开发工具,并实现了很强大的插件机制,也促使这些软件变的越来越强大。
一般而,一款软件、一个框架使用插件机制的原因主要基于以下3点:
- n 可以在无需对程序进行重新编译和发布的条件下扩展程序的功能。
- n 可以在不需要程序源代码的环境下为程序增加新的功能。
- n 在一个程序的业务逻辑不断发生改变、新的规则频频加入时能够灵活适应。
实现插件机制一般有3种技术:基于动态连接库DLL的插件、基于组件对象模型COM的插件、以及基于.NET反射技术的插件。
SuperIO是使用反射技术实现的插件机制,在后面的章节中进行详细介绍。
1.8 开发环境
开发语言
使用C#开发的SuperIO框架,当然使用其他语言也可以实现,例如:JAVA。
开发工具
一开始使用的是Visual Studio 2008工具进行开发,后来升级到Visual Studio 2012,并对SuperIO进行了重新编译。
支持框架
一开始使用的是Framework 2.0框架进行开发,后来升级到Framework 4.0,为了兼容较低版本的操作系统(Windows xp sp3),最高版本的框架只能使用Framework 4.0,再高版本的框架在Windows xp sp3下无法运行。如下图:
编译环境
使用X86平台对项目进行编译,如果开发插件也需要用X86平台进行编译,主要考虑到32位和64位操作系统的通用性。如下图:
开发环境:
一开始在Windows xp sp3操作系统下进行开发,后来升级到Windows 8/8.1。
1.9 第三方组件
使用Developer Express套件对框架的UI部分进行布局,主要应用在Menu、MdiTabForm、DockPanel这三个方面。
使用PCOMM.DLL对串口通道进行操作,没有使用微软自带的SerialPort组件,因为这个组件与一些工业串口卡不兼容,请参见:SerialPort操作PCI-1621D多串口卡,出现异常"参数不正确"
OPC服务端使用的是OPC基金会的WtOPCSvr.dll组件,但是这个需要正版授权。OPC客户端使用的是OPCDAAuto.dll组件。可以在http://pan.baidu.com/s/1pJ7lZWf下载SuperIO_Demo.rar事例代码,里边有完整的OPC服务端和客户端的代码。事例说明:http://www.bmpj.net/article-11-1.html。
1.10 小结
从软件设计角度,框架是一个可复用的软件架构解决方案,规定了应用的体系结构,阐明软件体系结构中各层次间及其层次内部各组件间的毅力关系,责任分配和控制流程,表现为一组接口,抽象类以及实例间协作的方法。
框架决定了一个软件的生命力,一个好的框架更能促进我们对它的持续维护、重构、完善。
下一单将介绍(SuperIO)框架总体的设计。
作者:唯笑志在
Email:504547114@qq.com
QQ:504547114
.NET开发技术联盟:54256083