前言

  工业物联网是一个范围很大的概念,本文从数据可视化的角度介绍了一个最小化的工业物联网平台,从 Modbus 数据采集到前端数据可视化呈现的基本实现思路。这里面主要涉及基于 Modbus 通讯规约的数据采集、后台实时数据处理、前端实时数据接收、前端实时数据可视化显示。物联网平台架构主要参考了图扑物联工业物联网平台,并从中提取了部分功能进行介绍,前端数据可视化采用的是HT for Web

  由于内容比较多,具体实现上涉及到前端工程师、后台工程师、数据采集工程师等多个开发角色的参与,所以本文重点介绍实现思路和 WebSocket 消息推送的实现,其它环节的具体实现细节作者会在其它文章中进行详细介绍。

一、物联网平台架构

  物联网平台主要是B/S模式,工业物联网平台大都采用的是微服务架构,本文主要涉及两个微服务:前置数据采集服务和 Web 实时消息推送服务。

  前置数据采集服务主要用于现场设备、仪器、仪表、传感器实时数据的采集,IoTopo工业物联网平台支持MQTT和透传云解析两种方式,透传云解析支持 Modbus 通讯规约。

  实时数据采集到平台后,需要推送到浏览器端进行显示,Web 实时消息推送服务采用 Web Socket 进行实时数据推送,可以确保数据的实时性和高效性。

  前端可视化技术采用的是HT for Web, HT for Web 是基于HTML5标准的企业应用图形界面一站式解决方案,其包含通用组件、拓扑组件和3D渲染引擎等丰富的图形界面开发类库。虽然 HT for Web 是商业软件但其提供的一站式解决方案可以极大缩短产品开发周期、减少研发成本、补齐我们在 Web 图形界面可视化技术上的短板。

二、Modbus 数据采集

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准,并且现在是工业电子设备之间常用的连接方式。Modbus比其他通信协议使用的更广泛的主要原因有:

  1. 公开发表并且无版权要求
  2. 易于部署和维护
  3. 对供应商来说,修改移动本地的比特或字节没有很多限制

Modbus允许多个 (大约240个) 设备连接在同一个网络上进行通信,举个例子,一个由测量温度和湿度的装置,并且将结果发送给计算机。在数据采集与监视控制系统(SCADA)中,Modbus通常用来连接监控计算机和远程终端控制系统(RTU)。

目前主流的编辑语言都有 Modbus 开发库,由于 Modbus 相对比较简单,很多企业也选择自行开发实现。Modbus 数据采集属于后台通讯,数据采集到平台后首先会进行数据清理和预处理,过滤掉冗余和无效数据,形成实时数据。平台获取到实时数据后一般会做 3 项工作:

1. 推送到 Web 前端进行显示

2. 存储到时序数据库

3. 判断是否产生告警

三、将实时数据推送到 Web 前端

  基于 Web 的实时数据推送需要用到 WebSocket,初学者可以学习阮一峰老师的 WebSocket 教程。我们基于 WebSocket 封装了一套消息传输协议,类似于一个消息中间件,前端部分可以订阅实时数据。考虑到海量实时数据的推送需求,将实时数据分为平台级、站点级、设备级,前端在订阅实时数据时,可以通过消息主题规则订阅不同级别的数据。平台侧在收到订阅请求时,可以主动推送一次实时数据。这样可以确保数据可视化界面在订阅实时数据成功后,第一时间显示出正确的界面。

  下面给出一个简化的 WebSocket 消息协议的客户端代码,大家可以在些基础上进行改造以适合自己的业务场景。

  消息主题正则表达式,用来匹配消息主题:

1 const matchWildcard = function(str, rule) {
2 return new RegExp('^' + rule.split('*').join('.*') + '$').test(str)
3 }

  WebSocket 客户端,支持消息主题订阅、取消消息主题订阅、同一个消息主题支持多个订阅者:

  1 class WebSocketClient {
2 constructor() {
3 this.ws = null
4 this.opts = {
5 debug: false,
6 autoReconnect: true,
7 reconnectInterval: 10000,
8 subscriber: {},
9 }
10 this.opened = false
11 }
12
13 connect() {
14 if (!this.opened) {
15 return
16 }
17
18 const url = 'ws://www.iotopo.com/msg/v1'
19 console.debug('websocket connect', url)
20
21 let ws = this.ws = new WebSocket(url)
22 ws.onmessage = event => {
23 if (this.opts.debug) {
24 console.log(event)
25 }
26 let data = JSON.parse(event.data)
27
28 for (let topic in this.opts.subscriber) {
29 if (matchWildcard(data.topic, topic)) {
30 let listeners = this.opts.subscriber[topic]
31 if (Array.isArray(listeners)) {
32 listeners.forEach(cb => {
33 if (typeof cb === 'function') {
34 cb(data.payload)
35 }
36 })
37 }
38 }
39 }
40 }
41 ws.onopen = e => {
42 if (this.opts.debug) {
43 console.log(e)
44 }
45 // 执行订阅请求
46 for (let topic in this.opts.subscriber) {
47 this._sendSubscribe(topic)
48 }
49 if (typeof this.opts.onopen === 'function') {
50 this.opts.onopen(e)
51 }
52 }
53 ws.onclose = e => {
54 if (this.opts.debug) {
55 console.log(e)
56 }
57 if (typeof this.opts.onclose === 'function') {
58 this.opts.onclose(e)
59 }
60 if (this.opened && this.opts.autoReconnect) {
61 setTimeout(() => {
62 this.connect()
63 }, this.opts.reconnectInterval)
64 }
65 }
66 ws.onerror = e => {
67 if (this.opts.debug) {
68 console.log(e)
69 }
70 if (typeof this.opts.onerror === 'function') {
71 this.opts.onerror(e)
72 }
73 }
74 }
75
76 open(opts) {
77 if (!this.opened) {
78 Object.assign(this.opts, opts || {})
79 this.opened = true
80 this.connect()
81 }
82 }
83
84 close() {
85 this.opened = false
86 if (this.ws !== null) {
87 this.ws.close()
88 }
89 this.ws = null
90 }
91
92 isOpened() {
93 return this.opened
94 }
95
96 isConnected() {
97 return this.ws !== null
98 }
99
100 _sendSubscribe(topic) {
101 if (this.ws === null) {
102 return Error('websocet not opened')
103 }
104 if (typeof topic !== 'string') {
105 return Error('topic should be a string value')
106 }
107
108 if (this.ws.readyState === WebSocket.OPEN) {
109 let msg = {
110 type: 'subscribe',
111 topic: topic,
112 }
113 this.ws.send(JSON.stringify(msg))
114 } else {
115 return Error('websocet not connected')
116 }
117 }
118
119 subscribe(topic, cb) {
120 if (this.opts.debug) {
121 console.log('subscribe:', topic)
122 }
123 let listeners = this.opts.subscriber[topic]
124 if (!Array.isArray(listeners)) {
125 listeners = [
126 cb
127 ]
128 this.opts.subscriber[topic] = listeners
129 } else {
130 listeners.push(cb)
131 }
132 this._sendSubscribe(topic)
133
134 return { topic, cb }
135 }
136
137 unsubscribe({topic, cb}) {
138 if (this.opts.debug) {
139 console.log('unsubscribe:', topic)
140 }
141
142 if (this.ws === null) {
143 return Error('websocet not opened')
144 }
145
146 if (typeof topic !== 'string') {
147 return Error('topic should be a string value')
148 }
149
150 let listeners = this.opts.subscriber[topic]
151 if (cb) {
152 if (Array.isArray(listeners)) {
153 let idx = listeners.indexOf(cb)
154 if (idx >= 0) {
155 listeners.splice(idx, 1)
156 }
157 }
158 } else {
159 delete this.opts.subscriber[topic]
160 }
161
162 if (Array.isArray(listeners) && listeners == 0) {
163 if (this.ws.readyState === WebSocket.OPEN) {
164 let msg = {
165 type: 'unsubscribe',
166 topic: topic,
167 }
168 this.ws.send(JSON.stringify(msg))
169 } else {
170 return Error('websocet not connected')
171 }
172 }
173 }
174 }

  用法举例:

 1 // 初始化客户端
2 const ws = new WebSocketClient()
3 // 与 WebSocket 服务器建议连接
4 ws.open({
5 debug: false
6 })
7 // 订阅消息
8 ws.subscribe('/foo/bar/*', function(msg) {
9 console.log('recv ws msg:', msg)
10 })

四、数据可视化界面实现

  基于 HT for Web 可以简单快速地搭建一个符合 HTML5 标准的可视化图形界面,通过 WebSocket 订阅实时数据,然后驱动图形界面的变化。数据驱动图形界面变化的实现方式很多,基本方法是采用数据绑定的方式,具体可以参考 HT for Web 的官方文档

在后面的文章中,作者会介绍一种基于 HT for Web 实现的业务数据和图形数据分离的数据绑定方法。

在线演示地址

从 Modbus 到 Web 数据可视化之 WebSocket 实时消息的更多相关文章

  1. Web 数据可视化

    /***************************************************************************************** * Web 数据可 ...

  2. python+mitmproxy抓包过滤+redis消息订阅+websocket实时消息发送,日志实时输出到web界面

    本实例实现需求 在游戏SDK测试中,经常需要测试游戏中SDK的埋点日志是否接入正确.本实例通过抓包(客户端http/https 请求)来判定埋点日志是是否接入正确. 实现细节:使用django项目,后 ...

  3. Python数据可视化实战:实时更新海外疫情数据,实现数据可视化

    前言 我国的疫情已经得到了科学的控制,开始了全面的复工复产,但是国外的疫情却“停不下来”.国外现在可谓就是处于水深火热当中啊,病毒极强的传染性,导致了许多的人都“中招”了,我国已经全面复工复产了,人大 ...

  4. 用图解&&实例讲解php是如何实现websocket实时消息推送的

    WebSocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 以前的推送技术使用 Ajax 轮询,浏览器需要不断地向服务器发送http请求来获取最新的数据,浪费很多的带 ...

  5. PoPo数据可视化周刊第6期

    PoPo数据可视化 聚焦于Web数据可视化与可视化交互领域,发现可视化领域有意思的内容.不想错过可视化领域的精彩内容, 就快快关注我们吧 :) 本期可视化精彩视频请关注公众号浏览 全天智能获Pre-A ...

  6. PoPo数据可视化周刊第5期

    PoPo数据可视化 聚焦于Web数据可视化与可视化交互领域,发现可视化领域有意思的内容.不想错过可视化领域的精彩内容, 就快快关注我们吧 :) World Wire 数据可视化演示(视频) IBM公司 ...

  7. PoPo数据可视化周刊第4期

    PoPo数据可视化 聚焦于Web数据可视化与可视化交互领域,发现可视化领域有意思的内容.不想错过可视化领域的精彩内容, 就快快关注我们吧 :) 微信号:popodv_com   由于国庆节的原因,累计 ...

  8. PoPo数据可视化周刊第3期 - 台风可视化

    9月台风席卷全球,本刊特别选取台风最佳可视化案例,数据可视化应用功力最深厚者,当属纽约时报,而传播效果最佳的是The Weather Channel关于Florence的视频预报,运用了数据可视化.可 ...

  9. PoPo数据可视化周刊第一期

    PoPo数据可视化 聚焦于Web数据可视化领域, 发现前端可视化领域有意思的内容. 涵盖前端可视化领域最新资讯, 开源可视化库的发布更新消息, 可视化案例分析与讲解, 可视化技术文章, 可视化大神的日 ...

  10. 爬取疫情数据,以django+pyecharts实现数据可视化web网页

    在家呆着也是呆着,不如做点什么消磨时间呗~ 试试用django+pyecharts实现疫情数据可视化web页面 这里要爬疫情数据 来自丁香园.搜狗及百度的疫情实时动态展示页 先看看劳动成果: 导航栏: ...

随机推荐

  1. 技术干货|基于Apache Hudi 的CDC数据入湖「内附干货PPT下载渠道」

    ​简介: 阿里云技术专家李少锋(风泽)在Apache Hudi 与 Apache Pulsar 联合 Meetup 杭州站上的演讲整理稿件,本议题将介绍典型 CDC 入湖场景,以及如何使用 Pulsa ...

  2. 快速搭建Zookeeper和Kafka环境

    前言 由于项目需要涉及到zookeeper和Kafka的使用,快速做了一篇笔记,方便小伙伴们搭建环境. zookeeper 官方定义 What is ZooKeeper? ZooKeeper is a ...

  3. 9.3K+ Star!一个面向中小型企业设计的开源运维平台!

    大家好,我是Java陈序员. 我们在日常开发中,会有很多的应用环境,开发环境.测试环境.回归环境.生产环境等等. 这些环境,需要部署在一台台的服务器上,有的可能是物理机,有的可能是云服务器. 那么,这 ...

  4. 【股票爬虫教程】我用100行Python代码,爬了雪球网5000只股票,还发现一个网站bug!

    目录 一.爬取目标 二.分析网页 三.爬虫代码 四.同步视频 五.get完整源码 一.爬取目标 您好,我是@马哥python说,今天继续分享爬虫案例. 爬取网站:雪球网的沪深股市行情数据 具体菜单:雪 ...

  5. 【深度学习】基础--NumPy

    因为深度学习会应用到我们大学时候学习的数学知识---线性代数.(矩阵当年想起来还是挺有意思的,有考研的经历都有感觉) 而在计算机里面如何展示矩阵的计算和应用,就需要运用到NumPy,是Python的一 ...

  6. SQL Server 数据库分析操作程序时SQL语句等待或执行时间较长(死锁)

    大型程序,总会遇到程序操作端卡顿或者直接遇到死锁,死锁是数据库设计的缺陷,虽不能完全避免死锁的出现,但一旦出现死锁我们要进行及时分析死锁语句,找出原因,进行SQL语句或程序的优化,避免再次出现同样的问 ...

  7. 启动vite和electron项目配置多个主进程

    推荐 concurrently 点击查看代码 "dev": "concurrently \"nodemon --exec electron . \" ...

  8. Vue3学习(二十四)- 文档页面功能开发

    写在前面 这部分真的感觉超级难,其实也不能说难,主要是真的想不到这个思路应该这么做,或者说他好厉害,他怎么知道该这么设计实现. 说下难点吧,我觉得后天逻辑还好,主要是前端部分真的需要点花点时间来思考, ...

  9. pageoffice6 实现在线模板套红

    在Web项目中处理Word文档,经常会用到Word模板,只不过这里的"模板"概念,都是指在Web项目中预先放置的doc.docx等扩展名的.真正的Word文档,对于Excel和PP ...

  10. docker之docker-compose

    docker-compose就是个二进制的工具,它可以单机编排,批量管理多个容器 [root@mcwk8s01 harbor]# file /usr/local/bin/docker-compose ...