2016-12-01 11:40:28 +02:00
|
|
|
/*
|
2017-01-09 23:11:09 +02:00
|
|
|
* Copyright © 2016-2017 The Thingsboard Authors
|
2016-12-01 11:40:28 +02:00
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
import 'angular-websocket';
|
|
|
|
|
import thingsboardTypes from '../common/types.constant';
|
|
|
|
|
|
|
|
|
|
export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboardTypes])
|
|
|
|
|
.factory('telemetryWebsocketService', TelemetryWebsocketService)
|
|
|
|
|
.name;
|
|
|
|
|
|
2017-01-31 20:56:11 +02:00
|
|
|
const RECONNECT_INTERVAL = 2000;
|
2017-01-31 14:54:48 +02:00
|
|
|
const WS_IDLE_TIMEOUT = 90000;
|
|
|
|
|
|
2016-12-01 11:40:28 +02:00
|
|
|
/*@ngInject*/
|
2017-01-31 14:54:48 +02:00
|
|
|
function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
|
2016-12-01 11:40:28 +02:00
|
|
|
|
|
|
|
|
var isOpening = false,
|
|
|
|
|
isOpened = false,
|
2017-01-31 14:54:48 +02:00
|
|
|
isActive = false,
|
|
|
|
|
isReconnect = false,
|
|
|
|
|
reconnectSubscribers = [],
|
2016-12-01 11:40:28 +02:00
|
|
|
lastCmdId = 0,
|
|
|
|
|
subscribers = {},
|
|
|
|
|
subscribersCount = 0,
|
|
|
|
|
cmdsWrapper = {
|
|
|
|
|
tsSubCmds: [],
|
|
|
|
|
historyCmds: [],
|
|
|
|
|
attrSubCmds: []
|
|
|
|
|
},
|
|
|
|
|
telemetryUri,
|
|
|
|
|
dataStream,
|
|
|
|
|
location = $window.location,
|
2017-01-31 14:54:48 +02:00
|
|
|
socketCloseTimer,
|
|
|
|
|
reconnectTimer;
|
2016-12-01 11:40:28 +02:00
|
|
|
|
|
|
|
|
if (location.protocol === "https:") {
|
|
|
|
|
telemetryUri = "wss:";
|
|
|
|
|
} else {
|
|
|
|
|
telemetryUri = "ws:";
|
|
|
|
|
}
|
|
|
|
|
telemetryUri += "//" + location.hostname + ":" + location.port;
|
|
|
|
|
telemetryUri += "/api/ws/plugins/telemetry";
|
|
|
|
|
|
2017-01-31 14:54:48 +02:00
|
|
|
|
2016-12-01 11:40:28 +02:00
|
|
|
var service = {
|
|
|
|
|
subscribe: subscribe,
|
|
|
|
|
unsubscribe: unsubscribe
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 14:54:48 +02:00
|
|
|
$rootScope.telemetryWsLogoutHandle = $rootScope.$on('unauthenticated', function (event, doLogout) {
|
|
|
|
|
if (doLogout) {
|
|
|
|
|
reset(true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2016-12-01 11:40:28 +02:00
|
|
|
return service;
|
|
|
|
|
|
|
|
|
|
function publishCommands () {
|
|
|
|
|
if (isOpened && (cmdsWrapper.tsSubCmds.length > 0 ||
|
|
|
|
|
cmdsWrapper.historyCmds.length > 0 ||
|
|
|
|
|
cmdsWrapper.attrSubCmds.length > 0)) {
|
|
|
|
|
dataStream.send(angular.copy(cmdsWrapper)).then(function () {
|
|
|
|
|
checkToClose();
|
|
|
|
|
});
|
|
|
|
|
cmdsWrapper.tsSubCmds = [];
|
|
|
|
|
cmdsWrapper.historyCmds = [];
|
|
|
|
|
cmdsWrapper.attrSubCmds = [];
|
|
|
|
|
}
|
|
|
|
|
tryOpenSocket();
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 20:07:05 +02:00
|
|
|
function onError (/*message*/) {
|
2016-12-01 11:40:28 +02:00
|
|
|
isOpening = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onOpen () {
|
|
|
|
|
isOpening = false;
|
|
|
|
|
isOpened = true;
|
2017-01-31 14:54:48 +02:00
|
|
|
if (reconnectTimer) {
|
|
|
|
|
$timeout.cancel(reconnectTimer);
|
|
|
|
|
reconnectTimer = null;
|
|
|
|
|
}
|
|
|
|
|
if (isReconnect) {
|
|
|
|
|
isReconnect = false;
|
|
|
|
|
for (var r in reconnectSubscribers) {
|
|
|
|
|
var reconnectSubscriber = reconnectSubscribers[r];
|
|
|
|
|
if (reconnectSubscriber.onReconnected) {
|
|
|
|
|
reconnectSubscriber.onReconnected();
|
|
|
|
|
}
|
|
|
|
|
subscribe(reconnectSubscriber);
|
|
|
|
|
}
|
|
|
|
|
reconnectSubscribers = [];
|
|
|
|
|
} else {
|
|
|
|
|
publishCommands();
|
|
|
|
|
}
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onClose () {
|
|
|
|
|
isOpening = false;
|
|
|
|
|
isOpened = false;
|
2017-01-31 14:54:48 +02:00
|
|
|
if (isActive) {
|
|
|
|
|
if (!isReconnect) {
|
|
|
|
|
reconnectSubscribers = [];
|
|
|
|
|
for (var id in subscribers) {
|
|
|
|
|
reconnectSubscribers.push(subscribers[id]);
|
|
|
|
|
}
|
|
|
|
|
reset(false);
|
|
|
|
|
isReconnect = true;
|
|
|
|
|
}
|
|
|
|
|
if (reconnectTimer) {
|
|
|
|
|
$timeout.cancel(reconnectTimer);
|
|
|
|
|
}
|
|
|
|
|
reconnectTimer = $timeout(tryOpenSocket, RECONNECT_INTERVAL, false);
|
|
|
|
|
}
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onMessage (message) {
|
|
|
|
|
if (message.data) {
|
|
|
|
|
var data = angular.fromJson(message.data);
|
|
|
|
|
if (data.subscriptionId) {
|
|
|
|
|
var subscriber = subscribers[data.subscriptionId];
|
2017-02-24 13:04:52 +02:00
|
|
|
if (subscriber && data) {
|
2017-03-03 18:09:14 +02:00
|
|
|
var keys = fetchKeys(subscriber);
|
|
|
|
|
if (!data.data) {
|
|
|
|
|
data.data = {};
|
|
|
|
|
}
|
|
|
|
|
for (var k in keys) {
|
|
|
|
|
var key = keys[k];
|
|
|
|
|
if (!data.data[key]) {
|
|
|
|
|
data.data[key] = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-24 13:04:52 +02:00
|
|
|
subscriber.onData(data);
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
checkToClose();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-03 18:09:14 +02:00
|
|
|
function fetchKeys(subscriber) {
|
|
|
|
|
var command;
|
|
|
|
|
if (angular.isDefined(subscriber.subscriptionCommand)) {
|
|
|
|
|
command = subscriber.subscriptionCommand;
|
|
|
|
|
} else {
|
|
|
|
|
command = subscriber.historyCommand;
|
|
|
|
|
}
|
|
|
|
|
if (command && command.keys && command.keys.length > 0) {
|
|
|
|
|
return command.keys.split(",");
|
|
|
|
|
} else {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 11:40:28 +02:00
|
|
|
function nextCmdId () {
|
|
|
|
|
lastCmdId++;
|
|
|
|
|
return lastCmdId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function subscribe (subscriber) {
|
2017-01-31 20:56:11 +02:00
|
|
|
isActive = true;
|
2016-12-01 11:40:28 +02:00
|
|
|
var cmdId = nextCmdId();
|
|
|
|
|
subscribers[cmdId] = subscriber;
|
|
|
|
|
subscribersCount++;
|
|
|
|
|
if (angular.isDefined(subscriber.subscriptionCommand)) {
|
|
|
|
|
subscriber.subscriptionCommand.cmdId = cmdId;
|
|
|
|
|
if (subscriber.type === types.dataKeyType.timeseries) {
|
|
|
|
|
cmdsWrapper.tsSubCmds.push(subscriber.subscriptionCommand);
|
|
|
|
|
} else if (subscriber.type === types.dataKeyType.attribute) {
|
|
|
|
|
cmdsWrapper.attrSubCmds.push(subscriber.subscriptionCommand);
|
|
|
|
|
}
|
|
|
|
|
} else if (angular.isDefined(subscriber.historyCommand)) {
|
|
|
|
|
subscriber.historyCommand.cmdId = cmdId;
|
|
|
|
|
cmdsWrapper.historyCmds.push(subscriber.historyCommand);
|
|
|
|
|
}
|
|
|
|
|
publishCommands();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function unsubscribe (subscriber) {
|
2017-01-31 20:56:11 +02:00
|
|
|
if (isActive) {
|
|
|
|
|
var cmdId = null;
|
|
|
|
|
if (subscriber.subscriptionCommand) {
|
|
|
|
|
subscriber.subscriptionCommand.unsubscribe = true;
|
|
|
|
|
if (subscriber.type === types.dataKeyType.timeseries) {
|
|
|
|
|
cmdsWrapper.tsSubCmds.push(subscriber.subscriptionCommand);
|
|
|
|
|
} else if (subscriber.type === types.dataKeyType.attribute) {
|
|
|
|
|
cmdsWrapper.attrSubCmds.push(subscriber.subscriptionCommand);
|
|
|
|
|
}
|
|
|
|
|
cmdId = subscriber.subscriptionCommand.cmdId;
|
|
|
|
|
} else if (subscriber.historyCommand) {
|
|
|
|
|
cmdId = subscriber.historyCommand.cmdId;
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
2017-01-31 20:56:11 +02:00
|
|
|
if (cmdId && subscribers[cmdId]) {
|
|
|
|
|
delete subscribers[cmdId];
|
|
|
|
|
subscribersCount--;
|
|
|
|
|
}
|
|
|
|
|
publishCommands();
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkToClose () {
|
|
|
|
|
if (subscribersCount === 0 && isOpened) {
|
|
|
|
|
if (!socketCloseTimer) {
|
2017-01-31 14:54:48 +02:00
|
|
|
socketCloseTimer = $timeout(closeSocket, WS_IDLE_TIMEOUT, false);
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function tryOpenSocket () {
|
2017-01-31 20:56:11 +02:00
|
|
|
if (isActive) {
|
|
|
|
|
if (!isOpened && !isOpening) {
|
|
|
|
|
isOpening = true;
|
|
|
|
|
if (userService.isJwtTokenValid()) {
|
2017-01-31 14:54:48 +02:00
|
|
|
openSocket(userService.getJwtToken());
|
2017-01-31 20:56:11 +02:00
|
|
|
} else {
|
|
|
|
|
userService.refreshJwtToken().then(function success() {
|
|
|
|
|
openSocket(userService.getJwtToken());
|
|
|
|
|
}, function fail() {
|
|
|
|
|
isOpening = false;
|
|
|
|
|
$rootScope.$broadcast('unauthenticated');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (socketCloseTimer) {
|
|
|
|
|
$timeout.cancel(socketCloseTimer);
|
|
|
|
|
socketCloseTimer = null;
|
2017-01-31 14:54:48 +02:00
|
|
|
}
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 14:54:48 +02:00
|
|
|
function openSocket(token) {
|
|
|
|
|
dataStream = $websocket(telemetryUri + '?token=' + token);
|
|
|
|
|
dataStream.onError(onError);
|
|
|
|
|
dataStream.onOpen(onOpen);
|
|
|
|
|
dataStream.onClose(onClose);
|
2017-02-09 18:33:03 +02:00
|
|
|
dataStream.onMessage(onMessage, {autoApply: false});
|
2017-01-31 14:54:48 +02:00
|
|
|
}
|
|
|
|
|
|
2016-12-01 11:40:28 +02:00
|
|
|
function closeSocket() {
|
2017-01-31 14:54:48 +02:00
|
|
|
isActive = false;
|
2016-12-01 11:40:28 +02:00
|
|
|
if (isOpened) {
|
|
|
|
|
dataStream.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-31 14:54:48 +02:00
|
|
|
|
2017-01-31 20:56:11 +02:00
|
|
|
function reset(close) {
|
2017-01-31 14:54:48 +02:00
|
|
|
if (socketCloseTimer) {
|
|
|
|
|
$timeout.cancel(socketCloseTimer);
|
|
|
|
|
socketCloseTimer = null;
|
|
|
|
|
}
|
|
|
|
|
lastCmdId = 0;
|
|
|
|
|
subscribers = {};
|
|
|
|
|
subscribersCount = 0;
|
|
|
|
|
cmdsWrapper.tsSubCmds = [];
|
|
|
|
|
cmdsWrapper.historyCmds = [];
|
|
|
|
|
cmdsWrapper.attrSubCmds = [];
|
2017-01-31 20:56:11 +02:00
|
|
|
if (close) {
|
2017-01-31 14:54:48 +02:00
|
|
|
closeSocket();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-01 11:40:28 +02:00
|
|
|
}
|