跨平台SCADA系统(组态软件)开发4

四、数据采集

采集功能是整个系统最核心的功能,完成了这一功能,其他部分便水到渠成。数据采集的需求包括:

(1)仪器各种各样,通信协议五花八门。

(2)软件开发完成以后,能够让不懂代码的人配置最终产品。

(3)使用多种链路,有些仪器共用一条链路。

(4)快速采集,有些链路或仪器出现问题不影响整个系统的采集。

(5)在采集的同时,反控能及时响应。

上述五个需求,我们在不同的模块中完成,每个模块只要做好自己的事情即可。

 

4.1 设备配置和通信协议

这一部分完成的是需求1和2。通信协议完成仪器数据格式的转化,一般情况下,不同的仪器使用不同的通信协议。但实际上,大部分仪器使用的通信协议都是类似的。例如大部分仪器使用Modbus协议,所不同的是它们使用不同的地址存储不同的数据。在这种情况下,我们不可能给每一种仪器做一次编码,只能把Modbus协议编码出来,而不同的地址对应什么,就使用设备配置的方法来解决。

系统使用反射的机制开发通信协议,做到灵活增删通信协议,不影响主程序的发布。我所经历过的场景是,每隔几天就会新增一种仪器,或是其中一种仪器修改了通信协议。在这种情况下,不可能因为仪器的变动,而推迟整个SCADA系统的发布。

所有通信协议类都继承一个基类,基类只有两个接口函数:ReadValueState(读取值和状态)和WriteCommand(写入命令)。为每个通信协议创建独立的项目(为支持跨平台,同时基于.NET Core和.NET Framework框架),生成之后,会得到一个dll。我们把这些dll放在某个指定的文件夹内,主程序经过扫描,即得到了通信协议类的列表。通过反射机制,得到每个类的实例,即可调用类的ReadValueState和WriteCommand两个接口函数。

当有一种新的仪器出现时,我们不需要修改主程序,而只需要新建一个协议项目,把编译好的dll扔到指定文件夹内,主程序就能支持这些新的仪器。

设备配置是按因子去配置的,以Modbus协议为例,我们需要为每个因子配置地址、连续数量、存储类型(寄存器、线圈等)、读写模式、字节顺序等。考虑到跨平台的需求,设备配置的最终结果保存为JSON文件,这样在Web端也能轻松读取。

 

4.2 链路

链路也有一个基类,其接口函数主要有两个,Send和Receive(当然还有Open和Close这些),它们操作的数据类型都是字节流byte[]。为了尽可能减少外部对链路的了解(黑盒化),我们需要在链路类里完成两个工作:

(1)异步转同步。为避免堵塞,链路内实际使用异步的方法接收数据。但这种异步回调的方式,不利于外部调用。我们需要把异步转成同步的方式。外部都是先调用Send,然后等一段时间,再调用Receive。如果没有数据,说明超时了。

(2)自动重连。在网络环境中,断线是很常见的,链路类需要做到自动发现网络,自动重新连接。那么,外部类只要不断地Send和Receive就可以了。

在开发过程中,有一点需要注意的是,.NET Core本身已经不支持SerialPort(串口)了。我们需要使用第三方的类库,本系统使用了SerialPortStream。

 

4.3 调度器

最后两点需求,涉及到一个调度器的开发。我们可以想象,目前采集任务可以简单用以下代码表示:

//采集线程
Task.Run(() =>
{
    foreach(Device device in devices)
    {
        device.ReadValueState();
    }
});

//反控线程
Task.Run(() =>
{
    devices[i].WriteCommand("cmd");
});

采集线程不断地调用协议类里的ReadValueState,而这个接口函数内部,必定是调用了链路类的Send和Receive(有可能多次)。反控线程可以说是用户触发的,不定时地调用WriteCommand,其内部同样也是调用了链路类的Send和Receive。

调度器的任务,就是在一大批Send和Receive下来时,确定什么时候在哪条链路上发送什么字节流,然后等待多长时间,从这条链路接收数据并跟原来的Send匹配。

调度器只使用一个线程,每个发送-接收任务都有一个等待时间(在设备配置时确定)。具体流程如下:

1、调度器先处理新的Send的任务,如果目前Send列表中没有此任务,则加入到Send列表中。为了让反控能及时响应,反控命令的Send任何都是马上加入,而采集的则稍微延迟加入。

2、根据当前时间,判断哪些Send任务可以执行,向物理链路发送字节流。

3、已经发送的Send任务,改成Receive任务,加入到Receive列表中。

4、根据当前时间,判断哪些Receive任务可以执行,向物理链路接收字节流。

5、把接收到的字节流,作为回调函数的参数,给协议层使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章