一、   引子

之前都在讲网关,不少网友关注如何实现界面。想了解下位机变量变化,是怎样一步步触发人机界面动画的。

这个步步触发,实质上是变量组(Group)的批量数据变化(DataChange)事件,引发了变量(Tag)的值更新(ValueChanged)事件,最终触发了图元的动画脚本(Action)。这是一个连锁反应。

简言之,界面是一批叫Tag乘客,从网关坐TLV协议的列车,到了上位机车站下车,在ClientService这个舞台上,用各自的乐器(ITagReader)演奏了一出交响乐。

二、   承上启下的核心对象:Tag

Tag(标签或者叫变量)是整个项目的核心对象。所谓核心对象,就是它无所不在,是动态的,流动的,就像血液融汇贯通。

实质上,Tag对下位机,就是一个个传感器的数据、一个个开关信号;对上位机,就是一个个按钮、仪表盘、电机。

Tag在变量管理器(TagConfig)产生,在系统初始化时分配,存在于人机界面程序和网关服务的各个角落,它们的值和时间戳在不断的变化。

对上位机设计者,用到的是Tag的名字、Tag的数据类型;对下位机设计者,看到的是Tag的地址、Tag的长度。对变量报警和数据归档,需要知道Tag的时间戳。

所有的Tag继承于ITag接口。Tag的类型就是数据的类型,有FloatTag(浮点型)、BoolTag(逻辑型)、还有整型、字符型。不同类型Tag的读写对应IReaderWriter接口的ReadXXX/WriteXXX方法。

Tag可以主动去读(Read)写(Write),也可以被动的刷新(Update),强制刷新(Refresh)。

Tag的Read方法是调用所属Group、最终是调用所属IDriver的ReadXXX方法从下位机读入数据。但Tag的主要应用场景是被动刷新触发ValueChanged事件,以驱动人机界面。

三、   上下位机连接的纽带:TLV协议

前文已经阐述了网关如何通过轮询下位机、推送批量数据给上位机。上位机需要将推送来的数据流解析为一堆变化的Tag,以驱动整个人机界面和控制逻辑。

网关和上位机之间通讯,我这里使用了一个自定义的简单的TLV协议(Tag-Length-Value),承载于Socket。

这个协议包括两部分:

  • 数据推送:将网关一端变化的Tag打包封装,传输给客户端;客户端拆包,还原为一堆Tag。具体流程为:
  1. 网关的DataChange事件调用SendData方法,将变化的Tag打包为HistoryData数组(包含变量ID、值、时间戳);
  2. Socket将HistoryData数组转换为字节流推送给客户端;
  3. 客户端的ClientDriver 包含ReciveData方法,将字节流还原为HistoryData数组并触发客户端DataChange事件;
  4. 客户端的DataChange事件将HistoryData数组转换为Tag数组,并调用Tag的Update,触发ValueChanging和ValueChanged事件。
  • 指令:客户端主动向网关发送指令,一般用来读、写特定变量或一批变量,还可以查询历史归档、查询报警等。指令格式如下:

指令码FCTCOMMAND:包含各种命令;

参数:如读入时间段内所有归档数据,则需要起始时间、结束时间;读入变量,则需要变量ID。

返回值:网关接收指令并返回数据,也是字节流。

    public class FCTCOMMAND
{
public const byte fctHead = 0xAB;//报头可加密,如报头不符,则不进行任何操作;客户端Socket发送报警请求,封装于Server
public const byte fctHdaIdRequest = ;//按变量ID读入历史数据
public const byte fctHdaRequest = ;//读时间段内所有历史数据
public const byte fctAlarmRequest = ;//读报警数据
public const byte fctOrderChange = ;//读订单
public const byte fctReset = ;//重置指令,一般用来释放网关套接字
public const byte fctXMLHead = 0xEE;//xml协议
public const byte fctReadSingle = ;//读单一变量
public const byte fctReadMultiple = ;//读多个变量
public const byte fctWriteSingle = ;//写单一变量
public const byte fctWriteMultiple = ;//写多个变量
}

四、   人机界面的驱动引擎:ClientService

人机界面客户端的 ClientService与网关的DAService如出一辙:都具有相类似的结构,继承了IDataServer, IAlarmServer,都从同一个数据库加载驱动、组、变量、报警:

客户端的:

public sealed class DAServer : IDataServer, IAlarmServer, IHDAServer

网关的:

public class DAService : IDataExchangeService, IDataServer, IAlarmServer

只是多了一个IHDAServer,具有查询历史数据的功能,而历史数据归档是网关的功能。

因此,ClientService也带有自己的驱动ClientDriver,ClientDriver也带有自己的组ClientGroup。

注意的是,ClientDriver是上位机唯一的Driver,ClientGroup也是ClientDriver唯一的Group。这是因为上位机无需和各类型下位机打交道,与它打交道的唯一对象就是网关本身。

因此,人机界面的各类操作指令,如按按钮、读归档数据、查询报警等,最终都反映成TLV协议指令发送给网关,并得到反馈。

而人机界面图元的动画,都是来自网关推送的Tag,触发ValueChanged事件;事件的订阅者,就是图元对应的ITagReader,图元动画的幕后指挥。

五、   图元动画的幕后指挥:ITagReader

ITagReader接口为所有图元组件继承,它的功能就是将Tag与动画绑定。先看下结构:

    public interface ITagReader : ITagLink
{
string TagReadText { get; set; }
string[] GetActions();
Action SetTagReader(string key, Delegate tagChanged);
IList<ITagLink> Children { get; }
}

TagReadText属性,就是与图元动画关联的变量表达式:形如Tag1*2+Tag2*5>10。我实现了一个自定义表达式编译器Eval,可以解析表达式语法,分离出Tag1和Tag2。这段代码在Example-WindowHelper-BindingControl。

接着,图元组件订阅Tag1和Tag2的ValueChanged事件。

如果值发生变化,这个事件内部会执行SetTagReader,计算表达式的结果,如满足条件,将向界面发送指令。

例如变量表达式为Tag1*2+Tag2*5>10,此时若Tag1=1,Tag2=2,满足条件,最终会触发一个动画脚本:Action。这个Action可以是让电机报警,颜色变为闪烁的红色;也可以是点亮一盏灯,或打开一座阀门。下文会详细阐述。

从网关到人机界面流程:

六、   下面的计划

写一系列帖子,把架构、原理讲清楚。大致如下:

  • 网关层接口概述
  • 上下位机通讯原理
  • 如何实现一个设备驱动
  • 从网关到人机界面
  • 如何设计图元
  • VS插件模块及原理
  • 归档模块及文件格式
  • 如何进行功能扩展
  • 组态变量表达式实现

github地址:https://github.com/GavinYellow/SharpSCADA。QQ群:102486275

开源纯C#工控网关+组态软件(五)从网关到人机界面的更多相关文章

  1. 开源纯C#工控网关+组态软件

    一.   前言 在园子潜水也七八年了.说来惭愧,这么多年虽然一直自称.NET铁杆粉丝,然仅限于回几个不痛不痒的贴,既没有发布过代码,也没有写过文章. 看着.NET和C#在国外风生水起,国内却日趋没落, ...

  2. 开源纯C#工控网关+组态软件(七)数据采集与归档

    一.   引子 在当前自动化.信息化.智能化的时代背景下,数据的作用日渐凸显.而工业发展到如今,科技含量和自动化水平均显著提高,但对数据的采集.利用才开始起步. 对工业企业而言,数据采集日益受到重视, ...

  3. 开源纯C#工控网关+组态软件(八)表达式编译器

    一.   引子 监控画面的主要功能之一就是跟踪下位机变量变化,并将这些变化展现为动画.大部分时候,界面上一个图元组件的某个状态,与单一变量Tag绑定,比如电机的运行态,绑定一个MotorRunning ...

  4. 开源纯C#工控网关+组态软件(九)定制Visual Studio

    一.   引子 因为最近很忙(lan),很久没发博了.不少朋友对那个右键弹出菜单和连线的功能很感兴趣,因为VS本身是不包含这种功能的.   大家想这是什么鬼,怎么我的设计器没有,其实这是一个微软黑科技 ...

  5. 开源纯C#工控网关+组态软件(十)移植到.NET Core

    一.   引子 写这个开源系列已经十来篇了.自从十年前注册博客园以来,关注了张善友.老赵.xiaotie.深蓝色右手等一众大牛,也围观了逗比的吉日嘎啦.精密顽石等形形色色的园友.然而整整十年一篇文章都 ...

  6. 开源纯C#工控网关+组态软件(二)工控网关的实现

    一.   工控网关是什么 网关是物联网和工控系统的核心组件.网关起的是承上启下的作用.上即上位机,电脑/触屏监控系统.MES这些:下即下位机,包括PLC.传感器.嵌入式芯片等. 不同厂家的下位机,往往 ...

  7. 开源纯C#工控网关+组态软件(三)加入一个新驱动:西门子S7

    一.   引子 首先感谢博客园:第一篇文章.第一个开源项目,算是旗开得胜.可以看到,项目大部分流量来自于博客园,码农乐园,名不虚传^^. 园友给了我很多支持,并提出了很好的改进意见.现加入屏幕分辨率自 ...

  8. 开源纯C#工控网关+组态软件(四)上下位机通讯原理

    一.   网关的功能:承上启下 最近有点忙,更新慢了.感谢园友们给予的支持,现在github上已经有.目标是最好的开源组态,看来又近一步^^ 之前有提到网关是物联网的关键环节,它的作用就是承上启下. ...

  9. 开源纯C#工控网关+组态软件(六)图元组件

    一.   图元概述 图元是构成人机界面的基本单元.如一个个的电机.设备.数据显示.仪表盘,都是图元.构建人机界面的过程就是铺排.挪移.定位图元的过程. 图元设计是绘图和编码的结合.因为图元不仅有显示和 ...

随机推荐

  1. Java 使用Axis实现WebService实例

    在上一篇WebService实例中,基于jdk1.6以上的javax.jws 发布webservice接口.这篇博文则主要用eclipse/myeclipse 使用axis插件进行发布和调用WebSe ...

  2. 获取sd卡的总大小和可用大小

  3. 巧用Mono.Cecil反射加载类型和方法信息

    最近在做服务的细粒度治理,统一管理所有服务的方法.参数.返回值信息.方便后续的各个模块之间的对接和协作. 目前系统中所有的服务,管理到接口契约粒度,即服务接口声明和服务接口实现.要做服务的细粒度治理: ...

  4. MXNet--DMLC-Core代码解读与宏

    MXNet--DMLC-Core代码解读与宏 dmlc-core是Distributed (Deep) Machine Learning Community的一个基础模块,这个模块用被应用到了mxne ...

  5. Query DSL(2)----Full text queries

    Match Query match查询接受文本/数值/日期 { "match" : { "message" : "this is a test&quo ...

  6. spring web.xml配置

    <!--推荐使用此种方式-->  <listener> <listener-class> org.springframework.web.context.Conte ...

  7. JSP入门 文件上传

    commons-fileupload public void save(HttpServletRequest request,HttpServletResponse response) throws ...

  8. Reliability diagrams

    Reliability diagrams (Hartmann et al. 2002) are simply graphs of the Observed frequency of an event ...

  9. 【转】wireshark基本用法及过虑规则

    Wireshark 基本语法,基本使用方法,及包过虑规则: 1.过滤IP,如来源IP或者目标IP等于某个IP   例子: ip.src eq 192.168.1.107 or ip.dst eq 19 ...

  10. iOS在类内部怎么访问实例变量比较好?

    OC在类文件的内部访问实例变量,有直接访问和使用getter/setter方法访问两种方式,它们的区别有: 1.直接访问不经过OC的方法分发(method dispatch),所以访问速度比较快,在这 ...