(笔者参与了标准的编制,文章系原创,转载请标明来源)
三、1888介绍
本节都是讲述IEEE1888-2011标准的,其它子标准都是在其基础上的扩展,关于子标准的情况,将在下面各节中介绍,本节不涉及。
在介绍标准之前,先说说标准中point的概念,在1888标准中,point可以是任何可以度量的、随时间变化的物理量,并不限于现场设备中传感器、动作器中的物理量,也可以指诸如CPU占用率、磁盘剩余空间、网络流量等物理量或逻辑量。正确理解point,可以更好的理解1888的思路。
1.体系结构
IEEE1888-2011标准中系统的架构如上图,总的分类上,有Registry和component(作者观点:这样的划分值得讨论,将其单独列出来,可能出于它属于管理范畴的功能,而其它属于数据范畴的功能),Component下又分为Application(APP),Storage和Gateway(GW),这样,按照功能,将一个物联网采集系统大致分为如下几个功能块:
注册:就是registry。这个功能块完成信息(是管理信息,不是数据信息)的查询,有的类似于DNS。
网关:就是GW。这个功能块完成数据的采集。
存储:就是Storage。这个功能块完成采集数据的存储,更专业一些,就是实时数据库。
应用:就是APP。这个功能块完成最终用户的各种应用。
1888体系结构的这种划分,还是比较简洁明晰的,实际上,以上四个模块是现在大部分采集系统的基本组成(关于registry,在实际的应用中是有争议的,因此在1888的修订版中,Registry也是可选的),实际的采集系统还要包括其它功能块(例如安全等)。
在1888中,以上各模块是一种逻辑概念上的,并不是实际的部署就一定按照此模式。1888当初制定的目标就是规定一个大的框架,而不是细节,这样的好处在于目前的数采系统不用“伤筋动骨”就可以满足1888的体系要求,坏处当然是在细节功能上,必需做必要的扩展或完善才能满足实用的要求。
2.1888功能部件
本节仅介绍1888标准中的功能部件(上文提到的4个),在子标准中的扩展部件留在子标准中介绍。
上图是标准中的一个描述各组件关系的图,白底的Component们属于数据范畴,灰底的Registry属于管理范畴,因而它们的接口也不同。
Data接口:本质上就是“写”接口,有用信息由外部流向组件内部。
Query接口:本质上就是“读”接口,有用信息由内部流向组件外部。
Registration接口:用于注册器的“data”接口。
Lookup接口:用于注册器的“query”接口。
从上面看出,其实接口也定义地很宽泛。1888关注的是采集数据的处理,它的数据范畴的接口也是围绕数据的,管理范畴的接口是围绕注册的,从基本的应用场合来将,这些接口是够用的,当然,标准不会涉及实现问题,实现这些接口,就有五花八门的方式。
3.主要工作流程
按照标准描述,各部件之间的主要工作流程包括:
l 组件的注册过程:包括网关信息以及网关上各测点的信息;
l 测点的查询过程:指向Registry查询,将得到合适的URI。
l 向Storage查询某个测点的过程:
l 测点数据的传输:从合适的数据源获取
l 事件数据的传输:类似于数据源主动发送。
l 数据的写入:向数据源(也就是GW)写入数据。
以上是按“原味”描述的应用场景(use case),作为标准,总结出采集系统的共性,也就可以了,实际的应用当然要比以上所描述的场景复杂,每个过程大概都能写大段的篇幅。例如,撇开安全因素(将是1888.3关注的内容),稍微扩展一下:
对组件注册过程,标准只涉及了初始的注册过程(也就是组件进程启动的时候),在运行过程中,如何维护这些组件的存活性;数据的采集是采用“发”的方式还是“招”的方式(两种方式1888都支持),如果发生断网,怎么处理?1888的注册过程是自下而上的,由网关主动发起,上层(中心端)并不具有多少控制功能,这种模式并不是多数采集系统所采用的,如何做到兼顾实际应用?等等。这些问题的处理都需要适当的补充与扩展。
4.传输接口及协议
1888中大段篇幅是关于传输接口与协议的,这也是标准中比较硬性的规定。1888的“信息体”采用XML格式,好处当然是简单明了,易于扩展,坏处就是包比较大,性能有损失(笔者查了一下,目前似乎还没有XML“压缩”的标准,所以“大”包传输是必然的)。1888信息包的载体,标准上并未严格规定,例如可以采用HTTP+SOAP、HTTP+REST等,因此,要使两个供应商的1888组件互联,不做必要的配置是不行的。
关于传输协议,主要采用示例加以说明,关于这些传输协议XML的schema,可参见1888标准的附录。
各组件向registration注册 | |
【Request】 |
<transport> <body> ... (此处内容与组件类型有关,见下面的内容) </body> </transport>
对gateway注册 <component name="GW" uri="http://XX.XX.XX" expires="有效期(秒数)" support="FETCH|TRAP"> <key id="http://XX.XX.XX/P1" stream="in|out" limit="1"/> <key id="http://XX.XX.XX/P2" stream="in|out" limit="1" /> <key id="http://XX.XX.XX/P3" stream="out" limit="1" /> ... </component> 一般情况下,网关中component元素中的key是对point的引用,后面需要各point元素,继续对point注册。 <point id="http://XX.XX.XX/P1" type="Int" s:writable="true" s:location="Building2F221MeetingRoom1"/> <point id="http://XX.XX.XX/P2" type="Float" s:writable="true" s:location="Building2F221MeetingRoom1"/> <point id="http://XX.XX.XX/P3" type="Boolean" s:writable="false" s:location="Building2F221MeetingRoom1"/>
storage注册 <component name="Storage" uri="http://YY.YY.YY" support="FETCH|WRITE"> <key id="http://XX.XX.XX/P1"/> <key id="http://XX.XX.XX/P2" /> <key id="http://XX.XX.XX/P3" /> ... </component> Storage中的component元素中的有key就够了。
|
【Response】 |
<transport> <header> <OK/> </header> <body> …(如果有对注册信息的修改,在此显示) </body> </transport>
|
(registration)向gateway订阅数据 | |
【Request】 |
<transport> <header> <query id="9eed9de4-1c48-4b08-a41d-dac067fc1c0d" type="stream" ttl="该订阅有效期(秒数" callbackData="http://YY.YY.YY" > <key id=”http://XX.XX.XX/P1”/> … </query> </header> <body> <point id="..."> <value time="...">...</value> </point> </body> </transport>
|
【Response】 |
<transport> <header> <query id="9eed9de4-1c48-4b08-a41d-dac067fc1c0d" type="stream" ttl="60" callbackData="http://foo.org/axis/services/GUTAPI" > </query> <OK /> </header> </transport>
|
Gateway向Storage发送数据 | |
【Request】
|
<transport> <body> <point id="..."> <value time="...">...</value> </point> </body> </transport>
|
【Response】
|
<transport> <header> <OK/> </header> </transport>
|
Gateway接受外部的data写入方法(WRITE protocol) 与“Gateway向Storage发送数据”格式一样。
| |
Gateway接受外部的query方法(FETCH protocol) | |
【Request】
|
<transport> <header> <query id="6229c37f-970d-9292-83e4-7c0e54733f8a" type="storage" acceptableSize="20"> ... SEARCH_KEY ...(point ids 或者pointset) </query> </header> </transport>
|
【Response】
|
如果数据较少,acceptableSize设置的参数能容纳所有的数据时,采用如下应答: <transport> <header> <query id="61af19c3-2da2-4344-aa43-76e640521ce5" type="storage" acceptableSize="20" > ... SEARCH_KEY ... (与request相同或省略) </query> <OK /> </header> <body> <point id="..."> <value time="...">...</value> <value time="...">...</value> <value time="...">...</value> ... </point> </body> </transport>
如果数据较多,acceptableSize设置的参数不能容纳所有的数据时,采用如下应答: <transport> <header> <query id="6229c37f-970d-9292-83e4-7c0e54733f8a" type="storage" acceptableSize="20" cursor="dab751ed-0133-4ce4-8b7d-ba5c54ce4fb5"> ... SEARCH_KEY ... (与request相同或省略) </query> <OK /> </header> <body> <point id="..."> <value time="...">...</value> <value time="...">...</value> <value time="...">...</value> ... </point> </body> </transport> 采用cursor时,后续的request如下: <transport> <header> <query id="6229c37f-970d-9292-83e4-7c0e54733f8a" type="storage" acceptableSize="20"> cursor="dab751ed-0133-4ce4-8b7d-ba5c54ce4fb5"> ... SEARCH_KEY ... </query> </header> </transport> 后续的应答中,如果是最后一次,则应答中不包含cursor属性(将上面数据较少的例子),否则应答中包含cursor属性(将上面数据较多的例子)。 【注】 关于对acceptableSize的解释,可以理解为数据条数。queryid和cursor宜采用guid,保证其唯一性,通讯的两端需要靠其作为会话的标识。 |