[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

时间:2023-03-08 16:56:15
[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

目       录

第六章           通讯控制器的设计... 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; }
}

控制层次结构图如下:

[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

6.2    串口控制器

每个(硬件)串口都对应一个串口控制器,每个串口控制器里都会有一个独立的线程,也就是说用到多少个串口号就会有多少个控制器和线程。框架平台可能会挂载多个设备驱动(插件),有可能一个设备驱动对应一个串口,也可能几个设备驱动共用一个串口,那么也就是说串口控制器和设备驱动之间存在1对1或1对N的关系。结构示意图如下:

[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

一个串口控制器内的所有设备设置的串口通讯参数都是一样的,所以设备驱动(IRunDevice)接口的COM中的Port属性、IO接口(IIOChannel)的Key属性、以及串口控制器(IIOController)接口Key属性是一致的,都用于标识串口号。既然一个串口控制器中的所有设备都共用一个硬件串口,就决定了所有设备驱动之间的任务调度只能采用轮询模式,一个设备发送和接收操作完之后,再调度下一个设备驱动,设备驱动之间就是串行工作模式,避免一个串口控制器内多个设备驱动同时操作串口IO导致数据混乱,影响正常通讯。

一个串口控制器内的设备驱动是串行工作模式,如果把所有设备驱动都设置成一个串口号,在一个串口控制器下串行调度,那么就会影响设备驱动的通讯效率,某个设备的调度周期的公式如下:

某个设备调度周期=(串口控制器所有设备数-1)* 单个设备驱动执行耗时

这仅是一个理论值,实际应用中要比这个理论值要大,因为涉及到不类型的设备驱动共用一个串口号,在一个串口控制器下工作,处理的数据流程、方式不同,例如:有可能数据保存在TXT文件中、有可能保存在SQL数据库中、有可能保存在NoSQL数据库中等等。

有人会想,岂不是在一个串口下挂载的设备越多效率越低,的确是这样的。但是,多个串口控制器之间是并行工作模式。如果现场环境对通讯效率有要求的话,可以增加串口服务器,也就是增加可用的串口硬件电路,把N个设备驱动平衡负载到不同的串口上,增加并行运行的串口控制器的节点,进而提高框架平台的运行效率。

但是,这样解决之后也带来一定的风险和瓶颈,就是对于数据的存储,如果多个并行的数据流同时向一个单线程的存储介质写数据,那么又会造成互斥的现象,甚至造成意想不到的结果或异常,如下图:

[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

如果同时向Sql Server、Oracle、Mysql等数据库存储数据,那么是没有问题的;如果采用文本文件、桌面数据库等存储数据,那么可能存在问题,可以分多个文件进行保存操作。DCS系统大多采用PI(Plant Information System)数据库。总之,作为一个系统来讲,需要整体设计、考虑,这块需要特别注意。

6.3    网络控制器

框架平台只有一个网络控制器,网络控制器内有一个独立的线程负责对所有网络设备驱动进行轮询、并发、自控模式通讯调度。轮询通讯模式与串口控制器类似,只是串行的调度所有网络设备驱动,但是框架只有一个网络控制器,不能通过增加网络控制器来提高通讯效率,这种模式是网络通讯调度鸡肋;并发通讯模式,线程会通过控制器中的线程集中发送所有设备的请求命令数据,接收数据是通过IO异步监听来完成,异步接收到数据后再把数据分发到设备驱动的RunIODevice接口,进行数据处理;自控通讯模式,发送命令数据的职能移交给了设备驱动本身,可以通过定时器来完成发送命令数据的功能,线程不再负责发送命令数据,接收数据与并发通讯模式一样。网络控制器的内部示意图如下:

[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

针对网络通讯,轮询通讯模式是不能发挥其优势的,所以增加了并发通讯模式和自控通讯模式。后两种通讯模式会用到《第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>通讯控制管理器的接口定义如下:

[连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计

6.5    远程交互

在了解串口控制器和网络控制器的基本原理和功能后,还要考虑到一个应用场景:控制器不仅仅要与硬件进行数据交互,还有可能要把采集上来的数据转发到其他服务器或节点上,也就是框架平台要具备路由的功能,整合设备驱动采集上来的数据,进行打包、转发。

从这个应用场景来看,在开发设备驱动的时候,不适合在设备驱动的处理流程中进行转发、多业务处理,受环境、网络、业务复杂度的影响可能会阻塞控制器的调度,影响框架的整体运行效率。

在物联网建设中,多级互联、逐层转发是很常见技术需求。为了解决这个现实问题,框架平台提供了IAppService应用服务接口,二次开发者可以把设备驱动中的数据信息封装后传入到IAppService接口中,可以在这里实现缓存、转发等具体的业务服务。这样设计的主要目的是不影响框架平台实时的数据采集,保证数据源的稳定性。

IAppService具体的设计和应用将来《第7章 外部接口的设计》中进行详细介绍。

6.6    小结

通讯控制器实现这后,理论上框架平台就能够跑起来了,但是距离我们开始设计的目标还差很多工作要做,还不能为二次开发提供很大的便利。在后续的设计中,慢慢的会把框架平台丰富起来。

作者:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术联盟:54256083

文档下载:http://pan.baidu.com/s/1pJ7lZWf

官方网址:http://www.bmpj.net