Thingsboard 3.1.0 - 数据订阅

Thingsboard的数据订阅,使用websocket提供支持,用于完成数据可视和实时数据分析。

官方说明:https://thingsboard.io/docs/user-guide/telemetry/#websocket-api

一、Websocket API

ThingsBard Web UI正在积极的使用WebSocket。websocket api提供订阅设备数据变更的功能。

使用以下URL可以通过websocket链接到遥测服务。

ws(s)://host:port/api/ws/plugins/telemetry?token=$JWT_TOKEN

建立链接后,可以发送 [ 订阅命令 ],并接受 [ 订阅数据 ]。

1. 订阅命令

后端对应的java实现:TelemetryPluginCmdsWrapper.java

发送的各种订阅命令为json格式数据,如下:

{
	"attrSubCmds": [],
	"tsSubCmds": [],
	"historyCmds": [],
	"entityDataCmds": [],
	"entityDataUnsubscribeCmds": [],
	"alarmDataCmds": [],
	"alarmDataUnsubscribeCmds": []
}

其中tsSubCmds命令构成大致如下:

  • cmdId - 在websocket链接内唯一命令id
  • entityType - 唯一的实体类型。支持的有:TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM
  • entityId - 唯一的实体ID。
  • keys - 数据Key,以逗号分割的列表
  • timeWindow - 时间窗口,获取订阅的时序数据的间隔(毫秒)。获取[now()-timeWindow, now()]时间内数据
  • startTs - 获取历史数据的开始时间(milliseconds)
  • endTs - 获取历史数据的结束时间(milliseconds)
  • agg - 集合函数,例如:AVG,MAX,MIN
  • unsubscribe - 是否取消订阅,默认为false

每种命令的数据格式可能会稍有不同,具体请参看其java实现。

2. 订阅数据

后端对应的java实现:TelemetrySubscriptionUpdate.java

二、html实例

官方提供的sample:

  • token - JWT token,通过http://xxxxxx/api/auth/login登录后取得。
    请求数据:{“username”:“tenant@thingsboard.org”, “password”:“tenant”}
    返回数据:{“token”:"$YOUR_JWT_TOKEN", “refreshToken”:"$YOUR_JWT_REFRESH_TOKEN"}

  • entityId - 具体实体id,这里使用的是设备id

<!DOCTYPE HTML>
<html>
<head>
    <script type="text/javascript">
        function WebSocketAPIExample() {
            var token = "YOUR_JWT_TOKEN";
            var entityId = "YOUR_DEVICE_ID";
            var webSocket = new WebSocket("ws://127.0.0.1:8080/api/ws/plugins/telemetry?token=" + token);

            if (entityId === "YOUR_DEVICE_ID") {
                alert("Invalid device id!");
                webSocket.close();
            }

            if (token === "YOUR_JWT_TOKEN") {
                alert("Invalid JWT token!");
                webSocket.close();
            }

            webSocket.onopen = function () {
                var object = {
                    tsSubCmds: [
                        {
                            entityType: "DEVICE",
                            entityId: entityId,
                            scope: "LATEST_TELEMETRY",
                            cmdId: 10
                        }
                    ],
                    historyCmds: [],
                    attrSubCmds: []
                };
                var data = JSON.stringify(object);
                webSocket.send(data);
                alert("Message is sent: " + data);
            };

            webSocket.onmessage = function (event) {
                var received_msg = event.data;
                alert("Message is received: " + received_msg);
            };

            webSocket.onclose = function (event) {
                alert("Connection is closed!");
            };
        }
    </script>

</head>
<body>

<div id="sse">
    <a href="javascript:WebSocketAPIExample()">Run WebSocket</a>
</div>

</body>
</html>

三、c#实例

c#实现有关键两步,一是用户登录,二是ws链接订阅。

  1. 用户登录
    返回的Json解析,使用了Newtonsoft。
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace ThingsBoardTelemetry
{
	// 用户登录工具类
	public class AuthUtil
	{
	    private static string AUTH_DATA= "{{\"username\":\"{0}\", \"password\":\"{1}\"}}";
	    public static LoginResponse doLogin(string url, string username, string password)
	    {
	        string postData = string.Format(AUTH_DATA, username, password); ;
	        //创建HTTP请求
	        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
	        //设置请求方法
	        req.Method = "POST";
	        req.Timeout = 800000;
	        req.ContentType = "application/json";
	        req.Accept = "application/json";
	
	        byte[] data = Encoding.UTF8.GetBytes(postData);
	        req.ContentLength = data.Length;
	        using (Stream reqStream = req.GetRequestStream())
	        {
	            reqStream.Write(data, 0, data.Length);
	            reqStream.Close();
	        }
	        //获取请求返回的响应
	        HttpWebResponse res = (HttpWebResponse)req.GetResponse();
	        //读取来自API接口的响应中的内容
	        StreamReader read = new StreamReader(res.GetResponseStream());
	        //读取StreamReader中所有的流
	        string resResult = read.ReadToEnd().ToString();
	
	        //反序列化json字符串得到对象
	        LoginResponse jobj = JsonConvert.DeserializeObject<LoginResponse>(resResult);
	        
	        return jobj;
	    }
    }
    
    public class LoginResponse
    {
        public int errorCode { get; set; }
        public string message { get; set; }
        public int status { get; set; }
        public string timestamp { get; set; }

        public string token { get; set; }
        public string refreshToken { get; set; }
    }
}
  1. ws链接订阅
    c#的websocket包使用的是websocket-sharp。GitHub地址
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WebSocketSharp;

namespace ThingsBoardTelemetry
{
    public partial class FormMain : Form
    {

        private static string WSURL_TEMPLATE = "ws://{0}/api/ws/plugins/telemetry?token={1}";
        private static string AUTHURL_TEMPLATE = "http://{0}/api/auth/login";

        private static string SUB_CMD_TEMPLATE = "{{\"tsSubCmds\": [{{\"entityType\": \"DEVICE\",\"entityId\":\"{0}\",{1}\"scope\": \"LATEST_TELEMETRY\",\"cmdId\": 1}}], \"historyCmds\": [],\"attrSubCmds\": []}}";
        private static string KEY_TEMPLATE = "\"keys\":\"{0}\",";

        private LoginResponse loginRes = null;
        private WebSocket ws = null;

        public FormMain()
        {
            InitializeComponent();
        }

        public void AddToList(string msg)
        {
            string allmsg = this.textLog.Text + msg + "\r\n";
            textLog.Text = allmsg;
            Console.WriteLine(allmsg);
        }

        private void btnCon_Click(object sender, EventArgs e)
        {
            string serverAdd = this.txt_server.Text;
            string url = string.Format(AUTHURL_TEMPLATE, serverAdd);
            string user = this.txt_user.Text;
            string pass = this.txt_pass.Text;

            loginRes = AuthUtil.DoLogin(url, user, pass);
            if (loginRes.errorCode > 0)
            {
                MessageBox.Show(loginRes.message);
                loginRes = null;
                return;
            }

            this.AddToList("用户登录成功!");

        }

        private void Ws_OnClose(object sender, CloseEventArgs e)
        {
            this.AddToList("WebSocket链接关闭!" + e.Reason);
            if (ws != null)
            {
                ws.OnOpen -= Ws_OnOpen;
                ws.OnMessage -= Ws_OnMessage;
                ws.OnError -= Ws_OnError;
                ws.OnClose -= Ws_OnClose;
                ws.Close();
                ws = null;
            }
        }

        private void Ws_OnError(object sender, ErrorEventArgs e)
        {
            this.AddToList("出错!" + e.Message);
        }

        private void Ws_OnMessage(object sender, MessageEventArgs e)
        {
            CheckForIllegalCrossThreadCalls = false;
            string msg = "↓ " + (!e.IsPing ? e.Data : "Received a ping.");
            this.AddToList(msg);
        }

        private void Ws_OnOpen(object sender, EventArgs e)
        {
            CheckForIllegalCrossThreadCalls = false;
            this.AddToList("WebSocket链接成功!");

            try
            {
                string deviceId = this.txt_deviceId.Text.Trim();
                string key = this.txtKey.Text.Trim();

                string keys = (key.Length == 0) ? "" : string.Format(KEY_TEMPLATE, key);
                string subCmd = string.Format(SUB_CMD_TEMPLATE, deviceId, keys);
                this.AddToList("↑ " + subCmd);
                ws.Send(subCmd);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void buttonSub_Click(object sender, EventArgs e)
        {
            if (loginRes == null)
            {
                MessageBox.Show("请先登录");
                return;
            }
            string deviceId = this.txt_deviceId.Text;
            if (deviceId.Trim().Length==0)
            {
                MessageBox.Show("设备ID必须输入。");
                return;
            }

            string serverAdd = this.txt_server.Text;
            string wsUrl = string.Format(WSURL_TEMPLATE, serverAdd, loginRes.token);
            ws = new WebSocket(wsUrl);
            ws.OnOpen += Ws_OnOpen;
            ws.OnMessage += Ws_OnMessage;
            ws.OnError += Ws_OnError;
            ws.OnClose += Ws_OnClose;
            ws.Connect();
        }

        private void btnUnsub_Click(object sender, EventArgs e)
        {
            ws.Close();
        }
    }
}
  1. 画面效果
    在这里插入图片描述
  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WebSocket客户端和服务端实例源码 WebSocket ws实例 HTML5 用java实现的服务端 Websocket与服务器的正常通信 众所周知,Web 应用的交互过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现,这种机制对于信息变化不是特别频繁的应用尚可,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在当前业界移动互联网蓬勃发展的趋势下,高并发与用户实时响应是 Web 应用经常面临的问题,比如金融证券的实时信息,Web 导航应用中的地理位置获取,社交网络的实时消息推送等。 传统的请求-响应模式的 Web 开发在处理此类业务场景时,通常采用实时通讯方案,常见的是: 轮询,原理简单易懂,就是客户端通过一定的时间间隔以频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。问题很明显,当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,带来很多无谓请求,浪费带宽,效率低下。 基于 Flash,AdobeFlash 通过自己的 Socket 实现完成数据交换,再利用 Flash 暴露出相应的接口为 JavaScript 调用,从而达到实时传输目的。此方式比轮询要高效,且因为 Flash 安装率高,应用场景比较广泛,但在移动互联网终端上 Flash 的支持并不好。IOS 系统中没有 Flash 的存在,在 Android 中虽然有 Flash 的支持,但实际的使用效果差强人意,且对移动设备的硬件配置要求较高。2012 年 Adobe 官方宣布不再支持 Android4.1+系统,宣告了 Flash 在移动终端上的死亡。 从上文可以看出,传统 Web 模式在处理高并发及实时性需求的时候,会遇到难以逾越的瓶颈,我们需要一种高效节能的双向通信机制来保证数据的实时传输。在此背景下,基于 HTML5 规范的、有 Web TCP 之称的 WebSocket 应运而生。 早期 HTML5 并没有形成业界统一的规范,各个浏览器和应用服务器厂商有着各异的类似实现,如 IBM 的 MQTT,Comet 开源框架等,直到 2014 年,HTML5 在 IBM、微软、Google 等巨头的推动和协作下终于尘埃落地,正式从草案落实为实际标准规范,各个应用服务器及浏览器厂商逐步开始统一,在 JavaEE7 中也实现了 WebSocket 协议,从而无论是客户端还是服务端的 WebSocket 都已完备,读者可以查阅HTML5 规范,熟悉新的 HTML 协议规范及 WebSocket 支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值