目 录
第六章 通讯控制器的设计... 2
6.1 控制器接口... 2
6.2 串口控制器... 3
6.3 网络控制器... 5
6.4 通讯控制管理器... 9
6.5 远程交互... 9
6.6 小结... 10
第六章 通讯控制器的设计
经过前几章的介绍,这章介绍通讯控制器。主要负责对设备驱动(IRunDevice)、IO通道(IIOChannel)进行协调、调度、以及事件做出响应,在此基础上实现轮询通讯模式、并发通讯模式和自控通讯模式的任务调度。由于串口和网络硬件链路特性的原因以及通讯机制不一样,所以通讯控制器的实现上也有很大差别。
6.1 控制器接口
控制器内置一个线程负责对设备驱动和IO实例进行任务协调、调度,相当于在《第4章 设备管理器的设计》、《第5章 串口和网络的IO设计》实现的基础上构建了一个更高层次的协调机制,并实现设备与IO的匹配、不同的通讯机制。
不管串口通讯控制器和网络通讯控制器如何实现,都会继承自统一的(IIOController)接口,接口定义的代码如下:
public interface IIOController{ /// <summary> /// 当然是否工作 /// </summary> bool IsWorked { set; get; } /// <summary> /// IO控制器的关键字。 /// </summary> string Key { get; } /// <summary> /// 启动服务 /// </summary> void StartService(); /// <summary> /// 停止服务 /// </summary> void StopService(); /// <summary> /// IO控制器类型 /// </summary> CommunicationType ControllerType { get; }}
控制层次结构图如下:
6.2 串口控制器
每个(硬件)串口都对应一个串口控制器,每个串口控制器里都会有一个独立的线程,也就是说用到多少个串口号就会有多少个控制器和线程。框架平台可能会挂载多个设备驱动(插件),有可能一个设备驱动对应一个串口,也可能几个设备驱动共用一个串口,那么也就是说串口控制器和设备驱动之间存在1对1或1对N的关系。结构示意图如下:
一个串口控制器内的所有设备设置的串口通讯参数都是一样的,所以设备驱动(IRunDevice)接口的COM中的Port属性、IO接口(IIOChannel)的Key属性、以及串口控制器(IIOController)接口Key属性是一致的,都用于标识串口号。既然一个串口控制器中的所有设备都共用一个硬件串口,就决定了所有设备驱动之间的任务调度只能采用轮询模式,一个设备发送和接收操作完之后,再调度下一个设备驱动,设备驱动之间就是串行工作模式,避免一个串口控制器内多个设备驱动同时操作串口IO导致数据混乱,影响正常通讯。
一个串口控制器内的设备驱动是串行工作模式,如果把所有设备驱动都设置成一个串口号,在一个串口控制器下串行调度,那么就会影响设备驱动的通讯效率,某个设备的调度周期的公式如下:
某个设备调度周期=(串口控制器所有设备数-1)* 单个设备驱动执行耗时
这仅是一个理论值,实际应用中要比这个理论值要大,因为涉及到不类型的设备驱动共用一个串口号,在一个串口控制器下工作,处理的数据流程、方式不同,例如:有可能数据保存在TXT文件中、有可能保存在SQL数据库中、有可能保存在NoSQL数据库中等等。
有人会想,岂不是在一个串口下挂载的设备越多效率越低,的确是这样的。但是,多个串口控制器之间是并行工作模式。如果现场环境对通讯效率有要求的话,可以增加串口服务器,也就是增加可用的串口硬件电路,把N个设备驱动平衡负载到不同的串口上,增加并行运行的串口控制器的节点,进而提高框架平台的运行效率。
但是,这样解决之后也带来一定的风险和瓶颈,就是对于数据的存储,如果多个并行的数据流同时向一个单线程的存储介质写数据,那么又会造成互斥的现象,甚至造成意想不到的结果或异常,如下图:
如果同时向Sql Server、Oracle、Mysql等数据库存储数据,那么是没有问题的;如果采用文本文件、桌面数据库等存储数据,那么可能存在问题,可以分多个文件进行保存操作。DCS系统大多采用PI(Plant Information System)数据库。总之,作为一个系统来讲,需要整体设计、考虑,这块需要特别注意。
6.3 网络控制器
框架平台只有一个网络控制器,网络控制器内有一个独立的线程负责对所有网络设备驱动进行轮询、并发、自控模式通讯调度。轮询通讯模式与串口控制器类似,只是串行的调度所有网络设备驱动,但是框架只有一个网络控制器,不能通过增加网络控制器来提高通讯效率,这种模式是网络通讯调度鸡肋;并发通讯模式,线程会通过控制器中的线程集中发送所有设备的请求命令数据,接收数据是通过IO异步监听来完成,异步接收到数据后再把数据分发到设备驱动的RunIODevice接口,进行数据处理;自控通讯模式,发送命令数据的职能移交给了设备驱动本身,可以通过定时器来完成发送命令数据的功能,线程不再负责发送命令数据,接收数据与并发通讯模式一样。网络控制器的内部示意图如下:
针对网络通讯,轮询通讯模式是不能发挥其优势的,所以增加了并发通讯模式和自控通讯模式。后两种通讯模式会用到《第4章 设备驱动管理器的设计》的“4.6 设备计数器的特殊用处”的设计,设备驱动计数器如果大于等于某个值的时候,就会通过RunIODevice(new byte[]{})驱动当前设备,执行整个设备处理流程,以改变设备驱动的运行状态,实际上当前设备驱动处于“通讯中断”状态。
发送数据代码如下:
public void ControllerSend(IRunDevice dev, byte[] data){ int counter = DeviceManager.GetInstance().GetCounter(dev.DeviceParameter.DeviceID.ToString()); int sendNum = SessionSocketManager.GetInstance().Send(dev.DeviceParameter.NET.RemoteIP, data); if (sendNum == data.Length && sendNum != 0) { DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "发送请求数据"); Interlocked.Increment(ref counter); } else { Interlocked.Increment(ref counter); DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "尝试发送数据失败"); } dev.ShowMonitorIOData(data, "发送"); if (counter >= 3) { try { dev.RunIODevice(new byte[] { }); } catch (Exception ex) { DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, ex.Message); GeneralLog.WriteLog(ex); } Interlocked.Exchange(ref counter, 0); } DeviceManager.GetInstance().SetCounter(dev.DeviceParameter.DeviceID.ToString(), counter);}
异步接收、分数据的代码如下:
private void NETDeviceController_ReceiveSocketData(object source, ReceiveSocketDataArgs e){ if (GlobalProperty.GetInstance().ControlMode == ControlMode.Parallel || GlobalProperty.GetInstance().ControlMode == ControlMode.Self) { int counter = 0; IRunDevice dev = null; IRunDevice[] list = DeviceManager.GetInstance().GetDevices(e.RemoteIP, CommunicationType.NET); for (int i = 0; i < list.Length; i++) { dev = list[i]; if (String.CompareOrdinal(dev.DeviceParameter.NET.RemoteIP, e.RemoteIP) == 0) { dev.ShowMonitorIOData(e.ReceiveData, "接收"); dev.AsyncRunIODevice(e.ReceiveData); counter = DeviceManager.GetInstance().GetCounter(dev.DeviceParameter.DeviceID.ToString()); Interlocked.Decrement(ref counter); if (counter < 0) { Interlocked.Exchange(ref counter, 0); } DeviceManager.GetInstance().SetCounter(dev.DeviceParameter.DeviceID.ToString(), counter); } } }}
6.4 通讯控制管理器
通讯控制管理器负责对串口控制器和网络控制器进行管理,实际上是对Dictionary<Key,Value>进行的封装,所有涉及到操作控制器的地方都是通过控制管理器来完成的。IIOControllerManager<TKey, TValue>通讯控制管理器的接口定义如下:
6.5 远程交互
在了解串口控制器和网络控制器的基本原理和功能后,还要考虑到一个应用场景:控制器不仅仅要与硬件进行数据交互,还有可能要把采集上来的数据转发到其他服务器或节点上,也就是框架平台要具备路由的功能,整合设备驱动采集上来的数据,进行打包、转发。
从这个应用场景来看,在开发设备驱动的时候,不适合在设备驱动的处理流程中进行转发、多业务处理,受环境、网络、业务复杂度的影响可能会阻塞控制器的调度,影响框架的整体运行效率。
在物联网建设中,多级互联、逐层转发是很常见技术需求。为了解决这个现实问题,框架平台提供了IAppService应用服务接口,二次开发者可以把设备驱动中的数据信息封装后传入到IAppService接口中,可以在这里实现缓存、转发等具体的业务服务。这样设计的主要目的是不影响框架平台实时的数据采集,保证数据源的稳定性。
IAppService具体的设计和应用将来《第7章 外部接口的设计》中进行详细介绍。
6.6 小结
通讯控制器实现这后,理论上框架平台就能够跑起来了,但是距离我们开始设计的目标还差很多工作要做,还不能为二次开发提供很大的便利。在后续的设计中,慢慢的会把框架平台丰富起来。
作者:唯笑志在
Email:[email protected]
QQ:504547114
.NET开发技术联盟:54256083
文档下载:http://pan.baidu.com/s/1pJ7lZWf
官方网址:http://www.bmpj.net