TB-39: UI: Add Gateway flag. (#44)

* TB-39: UI: Add Gateway flag.

* TB-39: UI: Update package version.
This commit is contained in:
Igor Kulikov 2017-01-31 14:54:48 +02:00 committed by GitHub
parent 31b248922e
commit 7c6cdb148c
9 changed files with 140 additions and 17 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "thingsboard", "name": "thingsboard",
"private": true, "private": true,
"version": "1.0.1", "version": "1.1.0",
"description": "Thingsboard UI", "description": "Thingsboard UI",
"licenses": [ "licenses": [
{ {

View File

@ -52,6 +52,11 @@ const apiProxy = httpProxy.createProxyServer({
} }
}); });
apiProxy.on('error', function (err, req, res) {
console.warn('API proxy error: ' + err);
res.end('Error.');
});
console.info(`Forwarding API requests to http://${forwardHost}:${forwardPort}`); console.info(`Forwarding API requests to http://${forwardHost}:${forwardPort}`);
app.all('/api/*', (req, res) => { app.all('/api/*', (req, res) => {

View File

@ -256,6 +256,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
type: types.dataKeyType.timeseries, type: types.dataKeyType.timeseries,
onData: function (data) { onData: function (data) {
onData(data, types.dataKeyType.timeseries); onData(data, types.dataKeyType.timeseries);
},
onReconnected: function() {
onReconnected();
} }
}; };
@ -278,6 +281,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
type: types.dataKeyType.timeseries, type: types.dataKeyType.timeseries,
onData: function (data) { onData: function (data) {
onData(data, types.dataKeyType.timeseries); onData(data, types.dataKeyType.timeseries);
},
onReconnected: function() {
onReconnected();
} }
}; };
@ -299,6 +305,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
type: types.dataKeyType.attribute, type: types.dataKeyType.attribute,
onData: function (data) { onData: function (data) {
onData(data, types.dataKeyType.attribute); onData(data, types.dataKeyType.attribute);
},
onReconnected: function() {
onReconnected();
} }
}; };
@ -428,6 +437,25 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
} }
} }
function onReconnected() {
if (datasourceType === types.datasourceType.device) {
for (var key in dataKeys) {
var dataKeysList = dataKeys[key];
for (var i = 0; i < dataKeysList.length; i++) {
var dataKey = dataKeysList[i];
var datasourceKey = key + '_' + i;
datasourceData[datasourceKey] = [];
for (var l in listeners) {
var listener = listeners[l];
listener.dataUpdated(datasourceData[datasourceKey],
listener.datasourceIndex,
dataKey.index);
}
}
}
}
}
function onData(sourceData, type) { function onData(sourceData, type) {
for (var keyName in sourceData) { for (var keyName in sourceData) {
var keyData = sourceData[keyName]; var keyData = sourceData[keyName];

View File

@ -307,12 +307,12 @@ function DeviceService($http, $q, $filter, telemetryWebsocketService, types) {
onSubscriptionData(data, subscriptionId); onSubscriptionData(data, subscriptionId);
} }
}; };
telemetryWebsocketService.subscribe(subscriber);
deviceAttributesSubscription = { deviceAttributesSubscription = {
subscriber: subscriber, subscriber: subscriber,
attributes: null attributes: null
} }
deviceAttributesSubscriptionMap[subscriptionId] = deviceAttributesSubscription; deviceAttributesSubscriptionMap[subscriptionId] = deviceAttributesSubscription;
telemetryWebsocketService.subscribe(subscriber);
} }
return subscriptionId; return subscriptionId;
} }

View File

@ -20,11 +20,17 @@ export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboard
.factory('telemetryWebsocketService', TelemetryWebsocketService) .factory('telemetryWebsocketService', TelemetryWebsocketService)
.name; .name;
const RECONNECT_INTERVAL = 5000;
const WS_IDLE_TIMEOUT = 90000;
/*@ngInject*/ /*@ngInject*/
function TelemetryWebsocketService($websocket, $timeout, $window, types, userService) { function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
var isOpening = false, var isOpening = false,
isOpened = false, isOpened = false,
isActive = false,
isReconnect = false,
reconnectSubscribers = [],
lastCmdId = 0, lastCmdId = 0,
subscribers = {}, subscribers = {},
subscribersCount = 0, subscribersCount = 0,
@ -36,7 +42,8 @@ function TelemetryWebsocketService($websocket, $timeout, $window, types, userSer
telemetryUri, telemetryUri,
dataStream, dataStream,
location = $window.location, location = $window.location,
socketCloseTimer; socketCloseTimer,
reconnectTimer;
if (location.protocol === "https:") { if (location.protocol === "https:") {
telemetryUri = "wss:"; telemetryUri = "wss:";
@ -46,11 +53,18 @@ function TelemetryWebsocketService($websocket, $timeout, $window, types, userSer
telemetryUri += "//" + location.hostname + ":" + location.port; telemetryUri += "//" + location.hostname + ":" + location.port;
telemetryUri += "/api/ws/plugins/telemetry"; telemetryUri += "/api/ws/plugins/telemetry";
var service = { var service = {
subscribe: subscribe, subscribe: subscribe,
unsubscribe: unsubscribe unsubscribe: unsubscribe
} }
$rootScope.telemetryWsLogoutHandle = $rootScope.$on('unauthenticated', function (event, doLogout) {
if (doLogout) {
reset(true);
}
});
return service; return service;
function publishCommands () { function publishCommands () {
@ -74,12 +88,42 @@ function TelemetryWebsocketService($websocket, $timeout, $window, types, userSer
function onOpen () { function onOpen () {
isOpening = false; isOpening = false;
isOpened = true; isOpened = true;
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(); publishCommands();
} }
}
function onClose () { function onClose () {
isOpening = false; isOpening = false;
isOpened = false; isOpened = false;
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);
}
} }
function onMessage (message) { function onMessage (message) {
@ -137,28 +181,60 @@ function TelemetryWebsocketService($websocket, $timeout, $window, types, userSer
function checkToClose () { function checkToClose () {
if (subscribersCount === 0 && isOpened) { if (subscribersCount === 0 && isOpened) {
if (!socketCloseTimer) { if (!socketCloseTimer) {
socketCloseTimer = $timeout(closeSocket, 90000, false); socketCloseTimer = $timeout(closeSocket, WS_IDLE_TIMEOUT, false);
} }
} }
} }
function tryOpenSocket () { function tryOpenSocket () {
isActive = true;
if (!isOpened && !isOpening) { if (!isOpened && !isOpening) {
isOpening = true; isOpening = true;
dataStream = $websocket(telemetryUri + '?token=' + userService.getJwtToken()); if (userService.isJwtTokenValid()) {
openSocket(userService.getJwtToken());
} else {
userService.refreshJwtToken().then(function success() {
openSocket(userService.getJwtToken());
}, function fail() {
isOpening = false;
$rootScope.$broadcast('unauthenticated');
});
}
}
if (socketCloseTimer) {
$timeout.cancel(socketCloseTimer);
socketCloseTimer = null;
}
}
function openSocket(token) {
dataStream = $websocket(telemetryUri + '?token=' + token);
dataStream.onError(onError); dataStream.onError(onError);
dataStream.onOpen(onOpen); dataStream.onOpen(onOpen);
dataStream.onClose(onClose); dataStream.onClose(onClose);
dataStream.onMessage(onMessage); dataStream.onMessage(onMessage);
} }
if (socketCloseTimer) {
$timeout.cancel(socketCloseTimer);
}
}
function closeSocket() { function closeSocket() {
isActive = false;
if (isOpened) { if (isOpened) {
dataStream.close(); dataStream.close();
} }
} }
function reset(closeSocket) {
if (socketCloseTimer) {
$timeout.cancel(socketCloseTimer);
socketCloseTimer = null;
}
lastCmdId = 0;
subscribers = {};
subscribersCount = 0;
cmdsWrapper.tsSubCmds = [];
cmdsWrapper.historyCmds = [];
cmdsWrapper.attrSubCmds = [];
if (closeSocket) {
closeSocket();
}
}
} }

View File

@ -147,6 +147,12 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.subscriptionId = newSubscriptionId; scope.subscriptionId = newSubscriptionId;
} }
scope.$on('$destroy', function() {
if (scope.subscriptionId) {
deviceService.unsubscribeForDeviceAttributes(scope.subscriptionId);
}
});
scope.editAttribute = function($event, attribute) { scope.editAttribute = function($event, attribute) {
if (!scope.attributeScope.clientSide) { if (!scope.attributeScope.clientSide) {
$event.stopPropagation(); $event.stopPropagation();

View File

@ -59,6 +59,11 @@
<div translate ng-message="required">device.name-required</div> <div translate ng-message="required">device.name-required</div>
</div> </div>
</md-input-container> </md-input-container>
<md-input-container class="md-block">
<md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }}
</md-checkbox>
</md-input-container>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>device.description</label> <label translate>device.description</label>
<textarea ng-model="device.additionalInfo.description" rows="2"></textarea> <textarea ng-model="device.additionalInfo.description" rows="2"></textarea>

View File

@ -32,11 +32,13 @@ export default function DeviceDirective($compile, $templateCache, toast, $transl
scope.$watch('device', function(newVal) { scope.$watch('device', function(newVal) {
if (newVal) { if (newVal) {
if (scope.device.id) {
deviceService.getDeviceCredentials(scope.device.id.id).then( deviceService.getDeviceCredentials(scope.device.id.id).then(
function success(credentials) { function success(credentials) {
scope.deviceCredentials = credentials; scope.deviceCredentials = credentials;
} }
); );
}
if (scope.device.customerId && scope.device.customerId.id !== types.id.nullUid) { if (scope.device.customerId && scope.device.customerId.id !== types.id.nullUid) {
scope.isAssignedToCustomer = true; scope.isAssignedToCustomer = true;
customerService.getCustomer(scope.device.customerId.id).then( customerService.getCustomer(scope.device.customerId.id).then(

View File

@ -323,7 +323,8 @@
"accessTokenCopiedMessage": "Device access token has been copied to clipboard", "accessTokenCopiedMessage": "Device access token has been copied to clipboard",
"assignedToCustomer": "Assigned to customer", "assignedToCustomer": "Assigned to customer",
"unable-delete-device-alias-title": "Unable to delete device alias", "unable-delete-device-alias-title": "Unable to delete device alias",
"unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}" "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
"is-gateway": "Is gateway"
}, },
"dialog": { "dialog": {
"close": "Close dialog" "close": "Close dialog"