开源纯C#工控网关+组态软件(四)上下位机通讯原理
一、 网关的功能:承上启下
最近有点忙,更新慢了。感谢园友们给予的支持,现在github上已经有。目标是最好的开源组态,看来又近一步^^
之前有提到网关是物联网的关键环节,它的作用就是承上启下。
下位机有下位机的语言,上位机有上位机的思路。网关就是一个翻译,把下位机的语言转成通用语,再告诉上位机该怎么做。
这个翻译的过程,应该保证:
- 实时性。如果太慢,上下位机明显不合拍,就会出问题。
- 精确性。信号不能频繁丢失、丢步、跳步;不能有太大误差;也不会带入太多干扰和噪音。
- 稳定性。如发生故障,如通讯断开,要能自动重连;要足够强壮,不会动辄崩溃;发生意外崩溃,要能自动重启;要有容错机制和错误日志;等等。
要实现这些指标,还是很有挑战的。
二、 通讯蓝本:OPC规范
网关看上去是个很玄奥的东西,网上找来很多相关资料,发现都集中到一个点:OPC规范。
OPC就如一盏明灯,照亮了前进的方向。OPC规范是大家手笔,集中了业界专家的智慧,站在巨人的肩膀上,可以少走很多弯路。
OPC规范定义了数据的采集、归档、报警以及完整的接口示范。
OPC数据采集规范,包含了这样一些重要概念:
- 数据读写方式包括下位机批量推送数据、上位机单独读写数据。
- 可以异步读写,也可以同步读写。
- 数据包括元数据(数据的属性,如数据类型、长度、名称等)和过程数据(ID、数值、时间戳、质量戳)。
- 包含驱动(Driver)-组(Group)-变量(Item)的三级架构。可以对变量按需分组,有的组只读,有的组需要1秒刷新一次,有的只要5分钟就可以,要加以区别以提高效率。
- 要能判断驱动程序是否断线、要提供断线或关闭的事件供应用程序处理。
总之,信息量很丰富,启发很大,我的网关程序很大程度上都参考了OPC规范。
但是OPC有它固有的缺陷:依赖微软的COM组件技术,不能跨平台,同时COM是一种过时的技术,在不同主机上通讯的配置十分繁琐且不安全。
因此,我改造实现了自己的类OPC服务器。基本通讯过程是:批量轮询下位机-与上个周期的数据比对-提取变化的数据-批量推送给上位机。
三、 与下位机通讯:批量轮询
- 下位机的特点-为什么要采用轮询
下位机通讯的特点:
- 下位机很多采用主-从模式。即主机发送的信息可以传送到各个从机或指定的从机,而各个从机的信息只能发送给主机。主机采用查询方式接收发送数据,从机采用中断方式接收发送数据。这种模式天然适合轮询。
- 下位机多数只有一个通信口,有些还是串口,天然不适合推送模式。
- 下位机很多是单片机,订阅-发布模式往往逻辑较为复杂,程序编写难度大,对芯片及存储要求也必然提高。因此采用这种模式的下位机目前极少。
轮询就是网关作为主机,定期请求下位机的数据。如果实现批量请求,减少往返,轮询的效率并不低。几千个变量轮询周期500毫秒(西门子),无压力。
轮询是以组(Group)为单位的。Group都继承自IGroup 接口:
public interface IGroup : IDisposable
{
bool IsActive { get; set; } short ID { get; } int UpdateRate { get; set; } float DeadBand { get; set; } string Name { get; set; } IDriver Parent { get; } IDataServer Server { get; } IEnumerable<ITag> Items { get; } bool AddItems(IList<TagMetaData> items); bool AddTags(IEnumerable<ITag> tags); bool RemoveItems(params ITag[] items); bool SetActiveState(bool active, params short[] items); ITag FindItemByAddress(DeviceAddress addr); HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray); int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true); ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache); ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache); ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache); ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache); ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache); ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache); int WriteInt32(DeviceAddress address, int value); int WriteInt16(DeviceAddress address, short value); int WriteFloat(DeviceAddress address, float value); int WriteString(DeviceAddress address, string value); int WriteBit(DeviceAddress address, bool value); int WriteBits(DeviceAddress address, byte value); event DataChangeEventHandler DataChange;
}
其中,UpdateRate就是轮询周期。DeadBand是死区。死区代表敏感度,设的小敏感度高,但也带来更多的噪声。
每个Group的变量可支持单独读写(如各ReadXXX,WriteXXX方法),也支持批量推送(DataChange事件)。对下位机的轮询,都是以组为单位,每个组在激活状态下按照自己的轮询周期,采集、推送数据,互不干扰。
每个Group包含特性相似的一组变量:有相同的轮询周期、激活属性(需要轮询或无需轮询)、读写属性(均为只读、读/写或只写),需要的话可以同时使能或同时屏蔽。
因为部分变量无需随时监控,可以将其划入一组,不刷新(轮询);有些变量变化很快,需要高频扫描;有些变化很慢也不需要时时查看,可以几分钟轮询一次。将变量有效分组可以提高对重点监控变量的读写效率,减少对下位机资源的占用。
网关如果有多个客户端相连,各自需要的数据又不尽相同,由网关统一定期轮询,再批量推送给客户端是很高效的。
就比如开超市,南来北往的客人(客户端)需求各异,但超市(网关)来统一采购(轮询),不用客户跟各个批发市场(下位机)直接订货,集中来我这购物(订阅Tag)就行。
- 未来的扩展
虽然当前主流PLC不都支持订阅-推送模式,但这是历史潮流。AB Controllogix、新的西门子S71200-1500,都支持标签地址,也就是直接推送变化的标签(Tag)数据。
未来考虑制定一个新的接口支持这一模式。
四、 与上位机通讯:订阅-推送
- 上位机的特点-为什么要采用订阅-推送
上位机通信的特点主要为:
- 要及时、准确了解下位机的消息。无论是监控画面、还是报警、提示人工操作这些,越实时越好,越准确越好。如果采用请求-响应模式,请求的周期决定实时性不会太好。请求频繁网关压力大,反之实时性差。
- 一个网关可能要拖好几个上位机,工段多的,可能要开十几个显示屏同时监控。因此,每个上位机都向网关请求数据,流量会陡然上升,网络会阻塞,网关压力会很大。
- 大部分上位机需要的数据不会经常变化,尤其是一些开关量。如果每次反复请求,浪费资源,浪费时间。
- 如果采用对各变量单独请求数据,势必造成大量不同时段请求-响应过程交错发生,难以整合,也难以批量读写,效率极低。
因此,向订阅客户只推送变化的数据,无疑是一种高效的办法。只要数据发生变化,马上推送给客户端,既保证了实时性,又保证了推送数据的最小化。
就像打报修电话,送修单位(下位机)不一定时时刻刻有人上门;电话打过去,总台(网关)记下号码,有人上门(下位机有数据变化)了通知(推送)您。没人你也不用几秒钟打一个催(轮询),你烦大家烦。
- 如何实现订阅-推送
推送是只推送变化的变量。要知道哪些变量变化了,必须要缓存上一次变量的数据加以比对。因此,需要缓存每次轮询的数据。
网关在对下位机的轮询过程中,将订阅的变量都扫描了一遍;这些数据都存在内存的变量缓存:ICache:
public interface ICache : IReaderWriter
{
int Size { get; set; } int ByteCount { get; } Array Cache { get; } int GetOffset(DeviceAddress start, DeviceAddress end);
}
首先,ICache 继承了IReaderWriter,也就是缓存类也具有可读写性,以方便比对。同时还有一个Cache属性,这就是内存区域:映射、储存下位机的变量。
根据下位机的地址最小单元、字节序的不同,对应的ICache也相应不同。例如ModbusTcp是16位寄存器,网络字节序,相应的Cache也是16位数组,按照网络字节序读写;AB PLC对应的Cache则是32位数组。
因为下位机可能有很多个,存储地址也是不连续的;但通过对下位机地址DeviceAddress的排序,最终下位机地址映射到一块连续的内存地址,通过DeviceAddress的CacheIndex(缓存索引)相关联。
每次轮询,即调用IReaderWriter的ReadBytes方法依次读入下位机变量区域;读入的值与Cache中缓存的数据比对; 所有变化的部分加入一个ChangedList表,存储变化的CacheIndex。
这些功能在PLCGroup定时器内的Poll函数实现。
protected void timer_Timer(object sender, EventArgs e)
{
if (_isActive)
{
lock (sync)
{
Poll();
if (_changedList.Count > )
Update();
}
}
else return;
}
比较之后,如发现ChangedList的数量大于0,说明有变量数值更新,执行Update方法,根据CacheIndex找到所有变化的变量,通过IGroup 接口的DataChange事件打包推送出去。
具体的订阅-推送过程,是利用套接字(Socket)在DAService类实现的。套接字顾名思义,就是一条电话线:各客户端向网关服务器发送请求,建立一个长连接:只要客户不挂电话,就一直连着。客户始终监听电话;只要服务器数据有变化,立马有话务员及时告知,客户响应。在这里我实现了一个自定义的TLV(Type-Length-Value)协议,将变化的数据打包发送,客户端拆包,分解出变量的ID、实时值、时间戳等信息,并转换为图元的动画,后文再详细阐述。
- 上下位机通讯流程图:
五、 下面的计划
写一系列帖子,把架构、原理讲清楚。大致如下:
- 网关层接口概述
- 上下位机通讯原理
- 如何实现一个设备驱动
- 如何设计图元
- VS插件模块及原理
- 归档模块及文件格式
- 如何进行功能扩展
- 组态变量表达式实现
github地址:https://github.com/GavinYellow/SharpSCADA。QQ群:102486275
开源纯C#工控网关+组态软件(四)上下位机通讯原理的更多相关文章
- 开源纯C#工控网关+组态软件(七)数据采集与归档
一. 引子 在当前自动化.信息化.智能化的时代背景下,数据的作用日渐凸显.而工业发展到如今,科技含量和自动化水平均显著提高,但对数据的采集.利用才开始起步. 对工业企业而言,数据采集日益受到重视, ...
- 开源纯C#工控网关+组态软件(八)表达式编译器
一. 引子 监控画面的主要功能之一就是跟踪下位机变量变化,并将这些变化展现为动画.大部分时候,界面上一个图元组件的某个状态,与单一变量Tag绑定,比如电机的运行态,绑定一个MotorRunning ...
- 开源纯C#工控网关+组态软件(九)定制Visual Studio
一. 引子 因为最近很忙(lan),很久没发博了.不少朋友对那个右键弹出菜单和连线的功能很感兴趣,因为VS本身是不包含这种功能的. 大家想这是什么鬼,怎么我的设计器没有,其实这是一个微软黑科技 ...
- 开源纯C#工控网关+组态软件(十)移植到.NET Core
一. 引子 写这个开源系列已经十来篇了.自从十年前注册博客园以来,关注了张善友.老赵.xiaotie.深蓝色右手等一众大牛,也围观了逗比的吉日嘎啦.精密顽石等形形色色的园友.然而整整十年一篇文章都 ...
- 开源纯C#工控网关+组态软件(三)加入一个新驱动:西门子S7
一. 引子 首先感谢博客园:第一篇文章.第一个开源项目,算是旗开得胜.可以看到,项目大部分流量来自于博客园,码农乐园,名不虚传^^. 园友给了我很多支持,并提出了很好的改进意见.现加入屏幕分辨率自 ...
- 开源纯C#工控网关+组态软件(六)图元组件
一. 图元概述 图元是构成人机界面的基本单元.如一个个的电机.设备.数据显示.仪表盘,都是图元.构建人机界面的过程就是铺排.挪移.定位图元的过程. 图元设计是绘图和编码的结合.因为图元不仅有显示和 ...
- 开源纯C#工控网关+组态软件(二)工控网关的实现
一. 工控网关是什么 网关是物联网和工控系统的核心组件.网关起的是承上启下的作用.上即上位机,电脑/触屏监控系统.MES这些:下即下位机,包括PLC.传感器.嵌入式芯片等. 不同厂家的下位机,往往 ...
- 开源纯C#工控网关+组态软件(五)从网关到人机界面
一. 引子 之前都在讲网关,不少网友关注如何实现界面.想了解下位机变量变化,是怎样一步步触发人机界面动画的. 这个步步触发,实质上是变量组(Group)的批量数据变化(DataChange)事件, ...
- 开源纯C#工控网关+组态软件
一. 前言 在园子潜水也七八年了.说来惭愧,这么多年虽然一直自称.NET铁杆粉丝,然仅限于回几个不痛不痒的贴,既没有发布过代码,也没有写过文章. 看着.NET和C#在国外风生水起,国内却日趋没落, ...
随机推荐
- php数据库操作小要点
保留小数点后两位 $ba = floor(($v[2]/$sum[0][0])*10000); //取整数 $bb = $ba/100; //两位小数 列的值加一可以直接用自身,不用单独查询出来 $s ...
- Http协议基本知识简介
HTTP协议是指超文本传输协议,位于应用层,HTTP规定数据格式,然后用tcp进行传输. 请求响应模式:简单理解为客户端对服务器发起请求,服务器响应客户端. 主要特点 无连接:无连接的含义是限制每次连 ...
- js中判断undefined类型
typeof 运算符返回一个用来表示表达式的数据类型的字符串.可能的字符串有:"number"."string"."boolean".&qu ...
- 1 Spring Cloud Eureka服务治理
注:此随笔为读书笔记.<Spring Cloud微服务实战> 什么是微服务? 微服务是将一个原本独立的系统拆分成若干个小型服务(一般按照功能模块拆分),这些小型服务都在各自独立的进程中运行 ...
- Kafka水位(high watermark)与leader epoch的讨论
~~~这是一篇有点长的文章,希望不会令你昏昏欲睡~~~ 本文主要讨论0.11版本之前Kafka的副本备份机制的设计问题以及0.11是如何解决的.简单来说,0.11之前副本备份机制主要依赖水位(或水印) ...
- 可能是讲解ARM中断和中断嵌套最通俗易懂的文章
几天前一个学生问我ARM中断嵌套的问题,我才发现原来在我心中理所当然的事对学生来说理解实属不易. ARM有七种模式,我们这里只讨论SVC.IRQ和FIQ模式. 我们可以假设ARM核心有两根中断引脚 ...
- 【 js 基础 】关于this
this 关键字是 Javascript 中很特别的一个关键字,被自动定义在所有函数的作用域中.this提供了一种更优雅的方式隐式"传递"一个对象的引用.今天就来说说 this 的 ...
- OC Block网上转载
1.block是一个特殊的OC对象, 它建立在栈上, 而不是堆上, 这么做一个是为性能考虑,还有就是方便访问局部变量. 2.默认情况下block使用到的局部变量都会被复制,而不是保留.所以它无法改变局 ...
- servlet文件上传2——复合表单提交(数据获取和文件上传)
上传文件时表单enctype属性必须要更改为<enctype='multipart/form-data'>:采用post提交表单,元素需要有name属性: 利用第三方jar包(common ...
- Divisors poj2992
Divisors Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 9940 Accepted: 2924 Descript ...