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”:“[email protected]”, “password”:“tenant”}
    返回数据:{“token”:"$YOUR_JWT_TOKEN", “refreshToken”:"$YOUR_JWT_REFRESH_TOKEN"}

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


<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 WebSocketa>
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. 画面效果
    Thingsboard 3.1.0 - 数据订阅_第1张图片

你可能感兴趣的:(thingsboard,iot)