thingsboard-aplcache

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index 85227e7..450b976 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -15,6 +15,8 @@
  */
 package org.thingsboard.server.controller;
 
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -49,6 +51,11 @@ public class DashboardController extends BaseController {
 
     public static final String DASHBOARD_ID = "dashboardId";
 
+    @Value("${dashboard.max_datapoints_limit}")
+    @Getter
+    private long maxDatapointsLimit;
+
+
     @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET)
     @ResponseBody
@@ -57,6 +64,13 @@ public class DashboardController extends BaseController {
     }
 
     @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/dashboard/maxDatapointsLimit", method = RequestMethod.GET)
+    @ResponseBody
+    public long getMaxDatapointsLimit() throws ThingsboardException {
+        return maxDatapointsLimit;
+    }
+
+    @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
     @ResponseBody
     public DashboardInfo getDashboardInfoById(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 1054007..5eef1bd 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -77,6 +77,11 @@ security:
   # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator
   user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}"
 
+# Dashboard parameters
+dashboard:
+  # Maximum allowed datapoints fetched by widgets
+  max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
+
 # Device communication protocol parameters
 http:
   request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
diff --git a/ui/src/app/api/time.service.js b/ui/src/app/api/time.service.js
index b8d0c41..56447aa 100644
--- a/ui/src/app/api/time.service.js
+++ b/ui/src/app/api/time.service.js
@@ -26,15 +26,17 @@ const MIN_INTERVAL = SECOND;
 const MAX_INTERVAL = 365 * 20 * DAY;
 
 const MIN_LIMIT = 10;
-const AVG_LIMIT = 200;
-const MAX_LIMIT = 500;
+//const AVG_LIMIT = 200;
+//const MAX_LIMIT = 500;
 
 /*@ngInject*/
-function TimeService($translate, types) {
+function TimeService($translate, $http, $q, types) {
 
     var predefIntervals;
+    var maxDatapointsLimit;
 
     var service = {
+        loadMaxDatapointsLimit: loadMaxDatapointsLimit,
         minIntervalLimit: minIntervalLimit,
         maxIntervalLimit: maxIntervalLimit,
         boundMinInterval: boundMinInterval,
@@ -45,20 +47,38 @@ function TimeService($translate, types) {
         defaultTimewindow: defaultTimewindow,
         toHistoryTimewindow: toHistoryTimewindow,
         createSubscriptionTimewindow: createSubscriptionTimewindow,
-        avgAggregationLimit: function () {
-            return AVG_LIMIT;
+        getMaxDatapointsLimit: function () {
+            return maxDatapointsLimit;
+        },
+        getMinDatapointsLimit: function () {
+            return MIN_LIMIT;
         }
     }
 
     return service;
 
+    function loadMaxDatapointsLimit() {
+        var deferred = $q.defer();
+        var url = '/api/dashboard/maxDatapointsLimit';
+        $http.get(url, {ignoreLoading: true}).then(function success(response) {
+            maxDatapointsLimit = response.data;
+            if (!maxDatapointsLimit || maxDatapointsLimit <= MIN_LIMIT) {
+                maxDatapointsLimit = MIN_LIMIT + 1;
+            }
+            deferred.resolve();
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
     function minIntervalLimit(timewindow) {
-        var min = timewindow / MAX_LIMIT;
+        var min = timewindow / 500;
         return boundMinInterval(min);
     }
 
     function avgInterval(timewindow) {
-        var avg = timewindow / AVG_LIMIT;
+        var avg = timewindow / 200;
         return boundMinInterval(avg);
     }
 
@@ -230,7 +250,7 @@ function TimeService($translate, types) {
             },
             aggregation: {
                 type: types.aggregation.avg.value,
-                limit: AVG_LIMIT
+                limit: Math.floor(maxDatapointsLimit / 2)
             }
         }
         return timewindow;
@@ -246,22 +266,27 @@ function TimeService($translate, types) {
         }
 
         var aggType;
+        var limit;
         if (timewindow.aggregation) {
             aggType = timewindow.aggregation.type || types.aggregation.avg.value;
+            limit = timewindow.aggregation.limit || maxDatapointsLimit;
         } else {
             aggType = types.aggregation.avg.value;
+            limit = maxDatapointsLimit;
         }
 
+
         var historyTimewindow = {
             history: {
                 fixedTimewindow: {
                     startTimeMs: startTimeMs,
                     endTimeMs: endTimeMs
                 },
-                interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, aggType)
+                interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, types.aggregation.avg.value)
             },
             aggregation: {
-                type: aggType
+                type: aggType,
+                limit: limit
             }
         }
 
@@ -275,7 +300,7 @@ function TimeService($translate, types) {
             realtimeWindowMs: null,
             aggregation: {
                 interval: SECOND,
-                limit: AVG_LIMIT,
+                limit: maxDatapointsLimit,
                 type: types.aggregation.avg.value
             }
         };
@@ -283,14 +308,14 @@ function TimeService($translate, types) {
         if (stateData) {
             subscriptionTimewindow.aggregation = {
                 interval: SECOND,
-                limit: MAX_LIMIT,
+                limit: maxDatapointsLimit,
                 type: types.aggregation.none.value,
                 stateData: true
             };
         } else {
             subscriptionTimewindow.aggregation = {
                 interval: SECOND,
-                limit: AVG_LIMIT,
+                limit: maxDatapointsLimit,
                 type: types.aggregation.avg.value
             };
         }
@@ -298,7 +323,7 @@ function TimeService($translate, types) {
         if (angular.isDefined(timewindow.aggregation) && !stateData) {
             subscriptionTimewindow.aggregation = {
                 type: timewindow.aggregation.type || types.aggregation.avg.value,
-                limit: timewindow.aggregation.limit || AVG_LIMIT
+                limit: timewindow.aggregation.limit || maxDatapointsLimit
             };
         }
         if (angular.isDefined(timewindow.realtime)) {
diff --git a/ui/src/app/api/user.service.js b/ui/src/app/api/user.service.js
index fa4d63c..a49c4a0 100644
--- a/ui/src/app/api/user.service.js
+++ b/ui/src/app/api/user.service.js
@@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
     .name;
 
 /*@ngInject*/
-function UserService($http, $q, $rootScope, adminService, dashboardService, loginService, toast, store, jwtHelper, $translate, $state, $location) {
+function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location) {
     var currentUser = null,
         currentUserDetails = null,
         lastPublicDashboardId = null,
@@ -390,6 +390,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
     function loadSystemParams() {
         var promises = [];
         promises.push(loadIsUserTokenAccessEnabled());
+        promises.push(timeService.loadMaxDatapointsLimit());
         return $q.all(promises);
     }
 
diff --git a/ui/src/app/components/timewindow.directive.js b/ui/src/app/components/timewindow.directive.js
index 6513671..4a6b72e 100644
--- a/ui/src/app/components/timewindow.directive.js
+++ b/ui/src/app/components/timewindow.directive.js
@@ -228,7 +228,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
                     if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) {
                         model.aggregation.type = value.aggregation.type;
                     }
-                    model.aggregation.limit = value.aggregation.limit || timeService.avgAggregationLimit();
+                    model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2);
                 }
             }
             scope.updateDisplayValue();
diff --git a/ui/src/app/components/timewindow-panel.controller.js b/ui/src/app/components/timewindow-panel.controller.js
index 3cf368b..d48355c 100644
--- a/ui/src/app/components/timewindow-panel.controller.js
+++ b/ui/src/app/components/timewindow-panel.controller.js
@@ -31,6 +31,8 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
     vm.maxRealtimeAggInterval = maxRealtimeAggInterval;
     vm.minHistoryAggInterval = minHistoryAggInterval;
     vm.maxHistoryAggInterval = maxHistoryAggInterval;
+    vm.minDatapointsLimit = minDatapointsLimit;
+    vm.maxDatapointsLimit = maxDatapointsLimit;
 
     if (vm.historyOnly) {
         vm.timewindow.selectedTab = 1;
@@ -86,6 +88,14 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
         return timeService.maxIntervalLimit(currentHistoryTimewindow());
     }
 
+    function minDatapointsLimit () {
+        return timeService.getMinDatapointsLimit();
+    }
+
+    function maxDatapointsLimit () {
+        return timeService.getMaxDatapointsLimit();
+    }
+
     function currentHistoryTimewindow() {
         if (vm.timewindow.history.historyType === 0) {
             return vm.timewindow.history.timewindowMs;
diff --git a/ui/src/app/components/timewindow-panel.tpl.html b/ui/src/app/components/timewindow-panel.tpl.html
index 271ac4c..50888ac 100644
--- a/ui/src/app/components/timewindow-panel.tpl.html
+++ b/ui/src/app/components/timewindow-panel.tpl.html
@@ -60,19 +60,22 @@
 							</md-option>
 						</md-select>
 					</md-input-container>
-					<md-slider-container ng-show="vm.showLimit()">
+					<md-slider-container ng-if="vm.showLimit()">
 						<span translate>aggregation.limit</span>
-						<md-slider flex min="10" max="500" ng-model="vm.timewindow.aggregation.limit" aria-label="limit" id="limit-slider">
+						<md-slider flex min="{{vm.minDatapointsLimit()}}" max="{{vm.maxDatapointsLimit()}}" step="1"
+                                   ng-model="vm.timewindow.aggregation.limit" aria-label="limit" id="limit-slider">
 						</md-slider>
-						<md-input-container>
-							<input flex type="number" ng-model="vm.timewindow.aggregation.limit" aria-label="limit" aria-controls="limit-slider">
+						<md-input-container style="max-width: 80px;">
+							<input flex type="number" step="1"
+                                   min="{{vm.minDatapointsLimit()}}" max="{{vm.maxDatapointsLimit()}}"
+                                   ng-model="vm.timewindow.aggregation.limit" aria-label="limit" aria-controls="limit-slider">
 						</md-input-container>
 					</md-slider-container>
-					<tb-timeinterval ng-show="vm.showRealtimeAggInterval()" min="vm.minRealtimeAggInterval()" max="vm.maxRealtimeAggInterval()"
+					<tb-timeinterval ng-if="vm.showRealtimeAggInterval()" min="vm.minRealtimeAggInterval()" max="vm.maxRealtimeAggInterval()"
 									 predefined-name="'aggregation.group-interval'"
 									 ng-model="vm.timewindow.realtime.interval">
 					</tb-timeinterval>
-					<tb-timeinterval ng-show="vm.showHistoryAggInterval()" min="vm.minHistoryAggInterval()" max="vm.maxHistoryAggInterval()"
+					<tb-timeinterval ng-if="vm.showHistoryAggInterval()" min="vm.minHistoryAggInterval()" max="vm.maxHistoryAggInterval()"
 									 predefined-name="'aggregation.group-interval'"
 									 ng-model="vm.timewindow.history.interval">
 					</tb-timeinterval>