• [连载]《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; }
    }
    

         控制层次结构图如下:

     

    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:504547114@qq.com

    QQ:504547114

    .NET开发技术联盟:54256083

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

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

  • 相关阅读:
    让EditPlus支持SQL高亮提示
    SQL Server 触发器
    asp.net中使用Global.asax文件中添加应用出错代码,写入系统日志文件或数据库
    C#钩子类 几乎捕获键盘鼠标所有事件
    DataGridView一些常用操作
    在C#中实现串口通信的方法
    System.Diagnostics.Process.Start的妙用
    Byte[]、Image、Bitmap 之间的相互转换
    Dos命令打印文件以及Dos打印到USB打印端口
    Winform常用的一些功能收集(持续更新)
  • 原文地址:https://www.cnblogs.com/lsjwq/p/5001697.html
Copyright © 2020-2023  润新知