/* * Copyright © 2016-2019 The Thingsboard Authors * * 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. */ /* options = { type, targetDeviceAliasIds, // RPC targetDeviceIds, // RPC datasources, timeWindowConfig, useDashboardTimewindow, legendConfig, decimals, units, callbacks } */ export default class Subscription { constructor(subscriptionContext, options) { this.ctx = subscriptionContext; this.type = options.type; this.callbacks = options.callbacks; this.id = this.ctx.utils.guid(); this.cafs = {}; this.registrations = []; this.hasResolvedData = false; var subscription = this; var deferred = this.ctx.$q.defer(); if (this.type === this.ctx.types.widgetType.rpc.value) { this.callbacks.rpcStateChanged = this.callbacks.rpcStateChanged || function(){}; this.callbacks.onRpcSuccess = this.callbacks.onRpcSuccess || function(){}; this.callbacks.onRpcFailed = this.callbacks.onRpcFailed || function(){}; this.callbacks.onRpcErrorCleared = this.callbacks.onRpcErrorCleared || function(){}; this.targetDeviceAliasIds = options.targetDeviceAliasIds; this.targetDeviceIds = options.targetDeviceIds; this.targetDeviceAliasId = null; this.targetDeviceId = null; this.rpcRejection = null; this.rpcErrorText = null; this.rpcEnabled = false; this.executingRpcRequest = false; this.executingPromises = []; this.initRpc().then( function() { deferred.resolve(subscription); } ); } else if (this.type === this.ctx.types.widgetType.alarm.value) { this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){}; this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){}; this.callbacks.dataLoading = this.callbacks.dataLoading || function(){}; this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || function(){}; this.alarmSource = options.alarmSource; this.alarmSearchStatus = angular.isDefined(options.alarmSearchStatus) ? options.alarmSearchStatus : this.ctx.types.alarmSearchStatus.any; this.alarmsPollingInterval = angular.isDefined(options.alarmsPollingInterval) ? options.alarmsPollingInterval : 5000; this.alarmsMaxCountLoad = angular.isDefined(options.alarmsMaxCountLoad) ? options.alarmsMaxCountLoad : 0; this.alarmsFetchSize = angular.isDefined(options.alarmsFetchSize) ? options.alarmsFetchSize : 100; this.alarmSourceListener = null; this.alarms = []; this.originalTimewindow = null; this.timeWindow = {}; this.useDashboardTimewindow = options.useDashboardTimewindow; if (this.useDashboardTimewindow) { this.timeWindowConfig = angular.copy(options.dashboardTimewindow); } else { this.timeWindowConfig = angular.copy(options.timeWindowConfig); } this.subscriptionTimewindow = null; this.loadingData = false; this.displayLegend = false; this.initAlarmSubscription().then( function success() { deferred.resolve(subscription); }, function fail() { deferred.reject(); } ); } else { this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){}; this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){}; this.callbacks.dataLoading = this.callbacks.dataLoading || function(){}; this.callbacks.legendDataUpdated = this.callbacks.legendDataUpdated || function(){}; this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || function(){}; this.datasources = this.ctx.utils.validateDatasources(options.datasources); this.datasourceListeners = []; /* * data = array of datasourceData * datasourceData = { * tbDatasource, * dataKey, { name, config } * data = array of [time, value] * } */ this.data = []; this.hiddenData = []; this.originalTimewindow = null; this.timeWindow = {}; this.useDashboardTimewindow = options.useDashboardTimewindow; this.stateData = options.stateData; if (this.useDashboardTimewindow) { this.timeWindowConfig = angular.copy(options.dashboardTimewindow); } else { this.timeWindowConfig = angular.copy(options.timeWindowConfig); } this.subscriptionTimewindow = null; this.comparisonEnabled = options.comparisonEnabled; if (this.comparisonEnabled) { this.timeForComparison = options.timeForComparison; this.comparisonTimeWindow = {}; this.timewindowForComparison = null; } this.units = options.units || ''; this.decimals = angular.isDefined(options.decimals) ? options.decimals : 2; this.loadingData = false; if (options.legendConfig) { this.legendConfig = options.legendConfig; this.legendData = { keys: [], data: [] }; this.displayLegend = true; } else { this.displayLegend = false; } this.caulculateLegendData = this.displayLegend && this.type === this.ctx.types.widgetType.timeseries.value && (this.legendConfig.showMin === true || this.legendConfig.showMax === true || this.legendConfig.showAvg === true || this.legendConfig.showTotal === true); this.initDataSubscription().then( function success() { deferred.resolve(subscription); }, function fail() { deferred.reject(); } ); } return deferred.promise; } getFirstEntityInfo() { var entityId, entityName, entityLabel = null; if (this.type === this.ctx.types.widgetType.rpc.value) { if (this.targetDeviceId) { entityId = { entityType: this.ctx.types.entityType.device, id: this.targetDeviceId }; entityName = this.targetDeviceName; } } else if (this.type == this.ctx.types.widgetType.alarm.value) { if (this.alarmSource && this.alarmSource.entityType && this.alarmSource.entityId) { entityId = { entityType: this.alarmSource.entityType, id: this.alarmSource.entityId }; entityName = this.alarmSource.entityName; entityLabel = this.alarmSource.entityLabel; } } else { for (var i=0;i { this.timeWindow.stDiff = stDiff; deferred.resolve(); }, () => { this.timeWindow.stDiff = 0; deferred.resolve(); } ); } else { if (this.timeWindow) { this.timeWindow.stDiff = 0; } deferred.resolve(); } return deferred.promise; } initAlarmSubscription() { var deferred = this.ctx.$q.defer(); var subscription = this; this.loadStDiff().then(() => { if (!subscription.ctx.aliasController) { subscription.hasResolvedData = true; subscription.configureAlarmsData(); deferred.resolve(); } else { subscription.ctx.aliasController.resolveAlarmSource(subscription.alarmSource).then( function success(alarmSource) { subscription.alarmSource = alarmSource; if (alarmSource) { subscription.hasResolvedData = true; } subscription.configureAlarmsData(); deferred.resolve(); }, function fail() { deferred.reject(); } ); } }); return deferred.promise; } configureAlarmsData() { var subscription = this; var registration; if (this.useDashboardTimewindow) { registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); subscription.update(); } }); this.registrations.push(registration); } else { this.startWatchingTimewindow(); } registration = this.ctx.$scope.$watch(function () { return subscription.alarmSearchStatus; }, function (newAlarmSearchStatus, prevAlarmSearchStatus) { if (!angular.equals(newAlarmSearchStatus, prevAlarmSearchStatus)) { subscription.update(); } }, true); this.registrations.push(registration); } initDataSubscription() { var deferred = this.ctx.$q.defer(); var subscription = this; this.loadStDiff().then(() => { if (!subscription.ctx.aliasController) { subscription.hasResolvedData = true; subscription.configureData(); deferred.resolve(); } else { subscription.ctx.aliasController.resolveDatasources(subscription.datasources).then( function success(datasources) { subscription.datasources = datasources; if (datasources && datasources.length) { subscription.hasResolvedData = true; } subscription.configureData(); deferred.resolve(); }, function fail() { subscription.notifyDataLoaded(); deferred.reject(); } ); } }); return deferred.promise; } configureData() { var additionalDatasources = []; var dataIndex = 0; var additionalKeysNumber = 0; for (var i = 0; i < this.datasources.length; i++) { var datasource = this.datasources[i]; var additionalDataKeys = []; let datasourceAdditionalKeysNumber = 0; for (var a = 0; a < datasource.dataKeys.length; a++) { var dataKey = datasource.dataKeys[a]; dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false; dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true; dataKey.pattern = angular.copy(dataKey.label); if (this.comparisonEnabled && dataKey.settings.comparisonSettings && dataKey.settings.comparisonSettings.showValuesForComparison) { datasourceAdditionalKeysNumber++; additionalKeysNumber++; let additionalDataKey = this.ctx.utils.createAdditionalDataKey(dataKey,datasource, this.timeForComparison,this.datasources,additionalKeysNumber); dataKey.settings.comparisonSettings.color = additionalDataKey.color; additionalDataKeys.push(additionalDataKey); } var datasourceData = { datasource: datasource, dataKey: dataKey, data: [] }; if (dataKey.type === this.ctx.types.dataKeyType.entityField) { if(datasource.entity && datasource.entity[this.ctx.types.entityField[dataKey.name].value]){ datasourceData.data.push([Date.now(), datasource.entity[this.ctx.types.entityField[dataKey.name].value]]); } } this.data.push(datasourceData); this.hiddenData.push({data: []}); if (this.displayLegend) { var legendKey = { dataKey: dataKey, dataIndex: dataIndex++ }; this.legendData.keys.push(legendKey); var legendKeyData = { min: null, max: null, avg: null, total: null, hidden: false }; this.legendData.data.push(legendKeyData); } } if (datasourceAdditionalKeysNumber > 0) { let additionalDatasource = angular.copy(datasource); additionalDatasource.dataKeys = additionalDataKeys; additionalDatasource.isAdditional = true; additionalDatasources.push(additionalDatasource); } } for (var j=0; j < additionalDatasources.length; j++) { let additionalDatasource = additionalDatasources[j]; for (var k=0; k < additionalDatasource.dataKeys.length; k++) { let additionalDataKey = additionalDatasource.dataKeys[k]; var additionalDatasourceData = { datasource: additionalDatasource, dataKey: additionalDataKey, data: [] }; this.data.push(additionalDatasourceData); this.hiddenData.push({data: []}); if (this.displayLegend) { var additionalLegendKey = { dataKey: additionalDataKey, dataIndex: dataIndex++ }; this.legendData.keys.push(additionalLegendKey); var additionalLegendKeyData = { min: null, max: null, avg: null, total: null, hidden: false }; this.legendData.data.push(additionalLegendKeyData); } } } this.datasources = this.datasources.concat(additionalDatasources); var subscription = this; var registration; if (this.displayLegend) { this.legendData.keys = this.ctx.$filter('orderBy')(this.legendData.keys, '+label'); registration = this.ctx.$scope.$watch( function() { return subscription.legendData.keys; }, function (newValue, oldValue) { for(var i = 0; i < newValue.length; i++) { if(newValue[i].dataKey.hidden != oldValue[i].dataKey.hidden) { subscription.updateDataVisibility(i); } } }, true); this.registrations.push(registration); } if (this.type === this.ctx.types.widgetType.timeseries.value) { if (this.useDashboardTimewindow) { registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); subscription.update(); } }); this.registrations.push(registration); } else { this.startWatchingTimewindow(); } } } resetData() { for (var i=0;i -1) { this.registrations.splice(index, 1); } } } initRpc() { var deferred = this.ctx.$q.defer(); if (this.targetDeviceAliasIds && this.targetDeviceAliasIds.length > 0) { this.targetDeviceAliasId = this.targetDeviceAliasIds[0]; var subscription = this; this.ctx.aliasController.getAliasInfo(this.targetDeviceAliasId).then( function success(aliasInfo) { if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) { subscription.targetDeviceId = aliasInfo.currentEntity.id; subscription.targetDeviceName = aliasInfo.currentEntity.name; if (subscription.targetDeviceId) { subscription.rpcEnabled = true; } else { subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false; } subscription.hasResolvedData = subscription.rpcEnabled; subscription.callbacks.rpcStateChanged(subscription); deferred.resolve(); } else { subscription.rpcEnabled = false; subscription.callbacks.rpcStateChanged(subscription); deferred.resolve(); } }, function fail () { subscription.rpcEnabled = false; subscription.callbacks.rpcStateChanged(subscription); deferred.resolve(); } ); } else { if (this.targetDeviceIds && this.targetDeviceIds.length > 0) { this.targetDeviceId = this.targetDeviceIds[0]; } if (this.targetDeviceId) { this.rpcEnabled = true; } else { this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; } this.hasResolvedData = true; this.callbacks.rpcStateChanged(this); deferred.resolve(); } return deferred.promise; } clearRpcError() { this.rpcRejection = null; this.rpcErrorText = null; this.callbacks.onRpcErrorCleared(this); } sendOneWayCommand(method, params, timeout) { return this.sendCommand(true, method, params, timeout); } sendTwoWayCommand(method, params, timeout) { return this.sendCommand(false, method, params, timeout); } sendCommand(oneWayElseTwoWay, method, params, timeout) { if (!this.rpcEnabled) { return this.ctx.$q.reject(); } if (this.rpcRejection && this.rpcRejection.status !== 408) { this.rpcRejection = null; this.rpcErrorText = null; this.callbacks.onRpcErrorCleared(this); } var subscription = this; var requestBody = { method: method, params: params }; if (timeout && timeout > 0) { requestBody.timeout = timeout; } var deferred = this.ctx.$q.defer(); this.executingRpcRequest = true; this.callbacks.rpcStateChanged(this); if (this.ctx.$scope.widgetEditMode) { this.ctx.$timeout(function() { subscription.executingRpcRequest = false; subscription.callbacks.rpcStateChanged(subscription); if (oneWayElseTwoWay) { deferred.resolve(); } else { deferred.resolve(requestBody); } }, 500); } else { this.executingPromises.push(deferred.promise); var targetSendFunction = oneWayElseTwoWay ? this.ctx.deviceService.sendOneWayRpcCommand : this.ctx.deviceService.sendTwoWayRpcCommand; targetSendFunction(this.targetDeviceId, requestBody).then( function success(responseBody) { subscription.rpcRejection = null; subscription.rpcErrorText = null; var index = subscription.executingPromises.indexOf(deferred.promise); if (index >= 0) { subscription.executingPromises.splice( index, 1 ); } subscription.executingRpcRequest = subscription.executingPromises.length > 0; subscription.callbacks.onRpcSuccess(subscription); deferred.resolve(responseBody); }, function fail(rejection) { var index = subscription.executingPromises.indexOf(deferred.promise); if (index >= 0) { subscription.executingPromises.splice( index, 1 ); } subscription.executingRpcRequest = subscription.executingPromises.length > 0; subscription.callbacks.rpcStateChanged(subscription); if (!subscription.executingRpcRequest || rejection.status === 408) { subscription.rpcRejection = rejection; if (rejection.status === 408) { subscription.rpcErrorText = 'Device is offline.'; } else { subscription.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText; if (rejection.data && rejection.data.length > 0) { subscription.rpcErrorText += '
'; subscription.rpcErrorText += rejection.data; } } subscription.callbacks.onRpcFailed(subscription); } deferred.reject(rejection); } ); } return deferred.promise; } updateDataVisibility(index) { var hidden = this.legendData.keys[index].dataKey.hidden; if (hidden) { this.hiddenData[index].data = this.data[index].data; this.data[index].data = []; } else { this.data[index].data = this.hiddenData[index].data; this.hiddenData[index].data = []; } this.onDataUpdated(); } onAliasesChanged(aliasIds) { if (this.type === this.ctx.types.widgetType.rpc.value) { return this.checkRpcTarget(aliasIds); } else if (this.type === this.ctx.types.widgetType.alarm.value) { return this.checkAlarmSource(aliasIds); } else { return this.checkSubscriptions(aliasIds); } } onDataUpdated(apply) { if (this.cafs['dataUpdated']) { this.cafs['dataUpdated'](); this.cafs['dataUpdated'] = null; } var subscription = this; this.cafs['dataUpdated'] = this.ctx.tbRaf(function() { try { subscription.callbacks.onDataUpdated(subscription, apply); } catch (e) { subscription.callbacks.onDataUpdateError(subscription, e); } }); if (apply) { this.ctx.$scope.$digest(); } } updateTimewindowConfig(newTimewindow) { this.timeWindowConfig = newTimewindow; } onResetTimewindow() { if (this.useDashboardTimewindow) { this.ctx.dashboardTimewindowApi.onResetTimewindow(); } else { if (this.originalTimewindow) { this.stopWatchingTimewindow(); this.timeWindowConfig = angular.copy(this.originalTimewindow); this.originalTimewindow = null; this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); this.update(); this.startWatchingTimewindow(); } } } onUpdateTimewindow(startTimeMs, endTimeMs) { if (this.useDashboardTimewindow) { this.ctx.dashboardTimewindowApi.onUpdateTimewindow(startTimeMs, endTimeMs); } else { this.stopWatchingTimewindow(); if (!this.originalTimewindow) { this.originalTimewindow = angular.copy(this.timeWindowConfig); } this.timeWindowConfig = this.ctx.timeService.toHistoryTimewindow(this.timeWindowConfig, startTimeMs, endTimeMs); this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); this.update(); this.startWatchingTimewindow(); } } notifyDataLoading() { this.loadingData = true; this.callbacks.dataLoading(this); } notifyDataLoaded() { this.loadingData = false; this.callbacks.dataLoading(this); } updateTimewindow() { this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000; if (this.subscriptionTimewindow.realtimeWindowMs) { this.timeWindow.maxTime = (moment()).valueOf() + this.timeWindow.stDiff;//eslint-disable-line this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; } else if (this.subscriptionTimewindow.fixedWindow) { this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs; this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs; } } updateRealtimeSubscription(subscriptionTimewindow) { if (subscriptionTimewindow) { this.subscriptionTimewindow = subscriptionTimewindow; } else { this.subscriptionTimewindow = this.ctx.timeService.createSubscriptionTimewindow( this.timeWindowConfig, this.timeWindow.stDiff, this.stateData); } this.updateTimewindow(); return this.subscriptionTimewindow; } updateComparisonTimewindow() { this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000; if (this.timewindowForComparison.realtimeWindowMs) { this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf(); //eslint-disable-line this.comparisonTimeWindow.minTime = this.comparisonTimeWindow.maxTime - this.timewindowForComparison.realtimeWindowMs; } else if (this.timewindowForComparison.fixedWindow) { this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs; this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs; } } updateSubscriptionForComparison() { if (!this.subscriptionTimewindow) { this.subscriptionTimewindow = this.updateRealtimeSubscription(); } this.timewindowForComparison = this.ctx.timeService.createTimewindowForComparison(this.subscriptionTimewindow, this.timeForComparison); this.updateComparisonTimewindow(); return this.timewindowForComparison; } dataUpdated(sourceData, datasourceIndex, dataKeyIndex, apply) { for (var x = 0; x < this.datasourceListeners.length; x++) { this.datasources[x].dataReceived = this.datasources[x].dataReceived === true; if (this.datasourceListeners[x].datasourceIndex === datasourceIndex && sourceData.data.length > 0) { this.datasources[x].dataReceived = true; } } this.notifyDataLoaded(); var update = true; var currentData; if (this.displayLegend && this.legendData.keys[datasourceIndex + dataKeyIndex].dataKey.hidden) { currentData = this.hiddenData[datasourceIndex + dataKeyIndex]; } else { currentData = this.data[datasourceIndex + dataKeyIndex]; } if (this.type === this.ctx.types.widgetType.latest.value) { var prevData = currentData.data; if (!sourceData.data.length) { update = false; } else if (prevData && prevData[0] && prevData[0].length > 1 && sourceData.data.length > 0) { var prevTs = prevData[0][0]; var prevValue = prevData[0][1]; if (prevTs === sourceData.data[0][0] && prevValue === sourceData.data[0][1]) { update = false; } } } if (update) { if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { this.updateTimewindow(); if (this.timewindowForComparison && this.timewindowForComparison.realtimeWindowMs) { this.updateComparisonTimewindow(); } } currentData.data = sourceData.data; if (this.caulculateLegendData) { this.updateLegend(datasourceIndex + dataKeyIndex, sourceData.data, apply); } this.onDataUpdated(apply); } } alarmsUpdated(alarms, apply) { this.notifyDataLoaded(); var updated = !this.alarms || !angular.equals(this.alarms, alarms); this.alarms = alarms; if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { this.updateTimewindow(); } if (updated) { this.onDataUpdated(apply); } } updateLegend(dataIndex, data, apply) { var dataKey = this.legendData.keys[dataIndex].dataKey; var decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : this.decimals; var units = dataKey.units && dataKey.units.length ? dataKey.units : this.units; var legendKeyData = this.legendData.data[dataIndex]; if (this.legendConfig.showMin) { legendKeyData.min = this.ctx.widgetUtils.formatValue(calculateMin(data), decimals, units); } if (this.legendConfig.showMax) { legendKeyData.max = this.ctx.widgetUtils.formatValue(calculateMax(data), decimals, units); } if (this.legendConfig.showAvg) { legendKeyData.avg = this.ctx.widgetUtils.formatValue(calculateAvg(data), decimals, units); } if (this.legendConfig.showTotal) { legendKeyData.total = this.ctx.widgetUtils.formatValue(calculateTotal(data), decimals, units); } this.callbacks.legendDataUpdated(this, apply !== false); } update() { this.unsubscribe(); this.subscribe(); } subscribe() { if (this.type === this.ctx.types.widgetType.rpc.value) { return; } if (this.type === this.ctx.types.widgetType.alarm.value) { this.alarmsSubscribe(); } else { this.notifyDataLoading(); if (this.type === this.ctx.types.widgetType.timeseries.value && this.timeWindowConfig) { this.updateRealtimeSubscription(); if (this.comparisonEnabled) { this.updateSubscriptionForComparison(); } if (this.subscriptionTimewindow.fixedWindow) { this.onDataUpdated(); } } var index = 0; for (var i = 0; i < this.datasources.length; i++) { var datasource = this.datasources[i]; if (angular.isFunction(datasource)) continue; var subscription = this; var listener = { subscriptionType: this.type, subscriptionTimewindow: this.subscriptionTimewindow, datasource: datasource, entityType: datasource.entityType, entityId: datasource.entityId, dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) { subscription.dataUpdated(data, datasourceIndex, dataKeyIndex, apply); }, updateRealtimeSubscription: function () { this.subscriptionTimewindow = subscription.updateRealtimeSubscription(); return this.subscriptionTimewindow; }, setRealtimeSubscription: function (subscriptionTimewindow) { subscription.updateRealtimeSubscription(angular.copy(subscriptionTimewindow)); }, datasourceIndex: index }; if (this.comparisonEnabled && datasource.isAdditional) { listener.subscriptionTimewindow = this.timewindowForComparison; listener.updateRealtimeSubscription = function () { this.subscriptionTimewindow = subscription.updateSubscriptionForComparison(); return this.subscriptionTimewindow; }; listener.setRealtimeSubscription = function () { subscription.updateSubscriptionForComparison(); }; } var entityFieldKey = false; for (var a = 0; a < datasource.dataKeys.length; a++) { if (datasource.dataKeys[a].type !== this.ctx.types.dataKeyType.entityField) { this.data[index + a].data = []; } else { entityFieldKey = true; } } index += datasource.dataKeys.length; this.datasourceListeners.push(listener); if (datasource.dataKeys.length) { this.ctx.datasourceService.subscribeToDatasource(listener); } var forceUpdate = false; if (datasource.unresolvedStateEntity || entityFieldKey || !datasource.dataKeys.length || (datasource.type === this.ctx.types.datasourceType.entity && !datasource.entityId) ) { forceUpdate = true; } if (forceUpdate) { this.notifyDataLoaded(); this.onDataUpdated(); } } } } alarmsSubscribe() { this.notifyDataLoading(); if (this.timeWindowConfig) { this.updateRealtimeSubscription(); if (this.subscriptionTimewindow.fixedWindow) { this.onDataUpdated(); } } var subscription = this; this.alarmSourceListener = { subscriptionTimewindow: this.subscriptionTimewindow, alarmSource: this.alarmSource, alarmSearchStatus: this.alarmSearchStatus, alarmsPollingInterval: this.alarmsPollingInterval, alarmsMaxCountLoad: this.alarmsMaxCountLoad, alarmsFetchSize: this.alarmsFetchSize, alarmsUpdated: function(alarms, apply) { subscription.alarmsUpdated(alarms, apply); } } this.alarms = null; this.ctx.alarmService.subscribeForAlarms(this.alarmSourceListener); var forceUpdate = false; if (this.alarmSource.unresolvedStateEntity || (this.alarmSource.type === this.ctx.types.datasourceType.entity && !this.alarmSource.entityId) ) { forceUpdate = true; } if (forceUpdate) { this.notifyDataLoaded(); this.onDataUpdated(); } } unsubscribe() { if (this.type !== this.ctx.types.widgetType.rpc.value) { if (this.type == this.ctx.types.widgetType.alarm.value) { this.alarmsUnsubscribe(); } else { for (var i = 0; i < this.datasourceListeners.length; i++) { var listener = this.datasourceListeners[i]; this.ctx.datasourceService.unsubscribeFromDatasource(listener); } this.datasourceListeners = []; this.resetData(); } } } alarmsUnsubscribe() { if (this.alarmSourceListener) { this.ctx.alarmService.unsubscribeFromAlarms(this.alarmSourceListener); this.alarmSourceListener = null; } } checkRpcTarget(aliasIds) { if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) { return true; } else { return false; } } checkAlarmSource(aliasIds) { if (this.alarmSource && this.alarmSource.entityAliasId) { return aliasIds.indexOf(this.alarmSource.entityAliasId) > -1; } else { return false; } } checkSubscriptions(aliasIds) { var subscriptionsChanged = false; for (var i = 0; i < this.datasourceListeners.length; i++) { var listener = this.datasourceListeners[i]; if (listener.datasource.entityAliasId) { if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) { subscriptionsChanged = true; break; } } } return subscriptionsChanged; } isDataResolved() { return this.hasResolvedData; } destroy() { this.unsubscribe(); for (var cafId in this.cafs) { if (this.cafs[cafId]) { this.cafs[cafId](); this.cafs[cafId] = null; } } this.registrations.forEach(function (registration) { registration(); }); this.registrations = []; } } function calculateMin(data) { if (data.length > 0) { var result = Number(data[0][1]); for (var i=1;i 0) { var result = Number(data[0][1]); for (var i=1;i 0) { return calculateTotal(data)/data.length; } else { return null; } } function calculateTotal(data) { if (data.length > 0) { var result = 0; for (var i = 0; i < data.length; i++) { result += Number(data[i][1]); } return result; } else { return null; } }