Commit 2f4df2803663f01df3bd5974cd7a16d2ce4710e1

Authored by Igor Kulikov
1 parent 99dde0cf

Configurable max datapoints limit for Aggregation NONE

@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import lombok.Getter;
  19 +import org.springframework.beans.factory.annotation.Value;
18 import org.springframework.http.HttpStatus; 20 import org.springframework.http.HttpStatus;
19 import org.springframework.security.access.prepost.PreAuthorize; 21 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 22 import org.springframework.web.bind.annotation.PathVariable;
@@ -49,6 +51,11 @@ public class DashboardController extends BaseController { @@ -49,6 +51,11 @@ public class DashboardController extends BaseController {
49 51
50 public static final String DASHBOARD_ID = "dashboardId"; 52 public static final String DASHBOARD_ID = "dashboardId";
51 53
  54 + @Value("${dashboard.max_datapoints_limit}")
  55 + @Getter
  56 + private long maxDatapointsLimit;
  57 +
  58 +
52 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 59 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
53 @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET) 60 @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET)
54 @ResponseBody 61 @ResponseBody
@@ -57,6 +64,13 @@ public class DashboardController extends BaseController { @@ -57,6 +64,13 @@ public class DashboardController extends BaseController {
57 } 64 }
58 65
59 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 66 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  67 + @RequestMapping(value = "/dashboard/maxDatapointsLimit", method = RequestMethod.GET)
  68 + @ResponseBody
  69 + public long getMaxDatapointsLimit() throws ThingsboardException {
  70 + return maxDatapointsLimit;
  71 + }
  72 +
  73 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
60 @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET) 74 @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
61 @ResponseBody 75 @ResponseBody
62 public DashboardInfo getDashboardInfoById(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { 76 public DashboardInfo getDashboardInfoById(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
@@ -77,6 +77,11 @@ security: @@ -77,6 +77,11 @@ security:
77 # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator 77 # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator
78 user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}" 78 user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}"
79 79
  80 +# Dashboard parameters
  81 +dashboard:
  82 + # Maximum allowed datapoints fetched by widgets
  83 + max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
  84 +
80 # Device communication protocol parameters 85 # Device communication protocol parameters
81 http: 86 http:
82 request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" 87 request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
@@ -26,15 +26,17 @@ const MIN_INTERVAL = SECOND; @@ -26,15 +26,17 @@ const MIN_INTERVAL = SECOND;
26 const MAX_INTERVAL = 365 * 20 * DAY; 26 const MAX_INTERVAL = 365 * 20 * DAY;
27 27
28 const MIN_LIMIT = 10; 28 const MIN_LIMIT = 10;
29 -const AVG_LIMIT = 200;  
30 -const MAX_LIMIT = 500; 29 +//const AVG_LIMIT = 200;
  30 +//const MAX_LIMIT = 500;
31 31
32 /*@ngInject*/ 32 /*@ngInject*/
33 -function TimeService($translate, types) { 33 +function TimeService($translate, $http, $q, types) {
34 34
35 var predefIntervals; 35 var predefIntervals;
  36 + var maxDatapointsLimit;
36 37
37 var service = { 38 var service = {
  39 + loadMaxDatapointsLimit: loadMaxDatapointsLimit,
38 minIntervalLimit: minIntervalLimit, 40 minIntervalLimit: minIntervalLimit,
39 maxIntervalLimit: maxIntervalLimit, 41 maxIntervalLimit: maxIntervalLimit,
40 boundMinInterval: boundMinInterval, 42 boundMinInterval: boundMinInterval,
@@ -45,20 +47,38 @@ function TimeService($translate, types) { @@ -45,20 +47,38 @@ function TimeService($translate, types) {
45 defaultTimewindow: defaultTimewindow, 47 defaultTimewindow: defaultTimewindow,
46 toHistoryTimewindow: toHistoryTimewindow, 48 toHistoryTimewindow: toHistoryTimewindow,
47 createSubscriptionTimewindow: createSubscriptionTimewindow, 49 createSubscriptionTimewindow: createSubscriptionTimewindow,
48 - avgAggregationLimit: function () {  
49 - return AVG_LIMIT; 50 + getMaxDatapointsLimit: function () {
  51 + return maxDatapointsLimit;
  52 + },
  53 + getMinDatapointsLimit: function () {
  54 + return MIN_LIMIT;
50 } 55 }
51 } 56 }
52 57
53 return service; 58 return service;
54 59
  60 + function loadMaxDatapointsLimit() {
  61 + var deferred = $q.defer();
  62 + var url = '/api/dashboard/maxDatapointsLimit';
  63 + $http.get(url, {ignoreLoading: true}).then(function success(response) {
  64 + maxDatapointsLimit = response.data;
  65 + if (!maxDatapointsLimit || maxDatapointsLimit <= MIN_LIMIT) {
  66 + maxDatapointsLimit = MIN_LIMIT + 1;
  67 + }
  68 + deferred.resolve();
  69 + }, function fail() {
  70 + deferred.reject();
  71 + });
  72 + return deferred.promise;
  73 + }
  74 +
55 function minIntervalLimit(timewindow) { 75 function minIntervalLimit(timewindow) {
56 - var min = timewindow / MAX_LIMIT; 76 + var min = timewindow / 500;
57 return boundMinInterval(min); 77 return boundMinInterval(min);
58 } 78 }
59 79
60 function avgInterval(timewindow) { 80 function avgInterval(timewindow) {
61 - var avg = timewindow / AVG_LIMIT; 81 + var avg = timewindow / 200;
62 return boundMinInterval(avg); 82 return boundMinInterval(avg);
63 } 83 }
64 84
@@ -230,7 +250,7 @@ function TimeService($translate, types) { @@ -230,7 +250,7 @@ function TimeService($translate, types) {
230 }, 250 },
231 aggregation: { 251 aggregation: {
232 type: types.aggregation.avg.value, 252 type: types.aggregation.avg.value,
233 - limit: AVG_LIMIT 253 + limit: Math.floor(maxDatapointsLimit / 2)
234 } 254 }
235 } 255 }
236 return timewindow; 256 return timewindow;
@@ -246,22 +266,27 @@ function TimeService($translate, types) { @@ -246,22 +266,27 @@ function TimeService($translate, types) {
246 } 266 }
247 267
248 var aggType; 268 var aggType;
  269 + var limit;
249 if (timewindow.aggregation) { 270 if (timewindow.aggregation) {
250 aggType = timewindow.aggregation.type || types.aggregation.avg.value; 271 aggType = timewindow.aggregation.type || types.aggregation.avg.value;
  272 + limit = timewindow.aggregation.limit || maxDatapointsLimit;
251 } else { 273 } else {
252 aggType = types.aggregation.avg.value; 274 aggType = types.aggregation.avg.value;
  275 + limit = maxDatapointsLimit;
253 } 276 }
254 277
  278 +
255 var historyTimewindow = { 279 var historyTimewindow = {
256 history: { 280 history: {
257 fixedTimewindow: { 281 fixedTimewindow: {
258 startTimeMs: startTimeMs, 282 startTimeMs: startTimeMs,
259 endTimeMs: endTimeMs 283 endTimeMs: endTimeMs
260 }, 284 },
261 - interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, aggType) 285 + interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, types.aggregation.avg.value)
262 }, 286 },
263 aggregation: { 287 aggregation: {
264 - type: aggType 288 + type: aggType,
  289 + limit: limit
265 } 290 }
266 } 291 }
267 292
@@ -275,7 +300,7 @@ function TimeService($translate, types) { @@ -275,7 +300,7 @@ function TimeService($translate, types) {
275 realtimeWindowMs: null, 300 realtimeWindowMs: null,
276 aggregation: { 301 aggregation: {
277 interval: SECOND, 302 interval: SECOND,
278 - limit: AVG_LIMIT, 303 + limit: maxDatapointsLimit,
279 type: types.aggregation.avg.value 304 type: types.aggregation.avg.value
280 } 305 }
281 }; 306 };
@@ -283,14 +308,14 @@ function TimeService($translate, types) { @@ -283,14 +308,14 @@ function TimeService($translate, types) {
283 if (stateData) { 308 if (stateData) {
284 subscriptionTimewindow.aggregation = { 309 subscriptionTimewindow.aggregation = {
285 interval: SECOND, 310 interval: SECOND,
286 - limit: MAX_LIMIT, 311 + limit: maxDatapointsLimit,
287 type: types.aggregation.none.value, 312 type: types.aggregation.none.value,
288 stateData: true 313 stateData: true
289 }; 314 };
290 } else { 315 } else {
291 subscriptionTimewindow.aggregation = { 316 subscriptionTimewindow.aggregation = {
292 interval: SECOND, 317 interval: SECOND,
293 - limit: AVG_LIMIT, 318 + limit: maxDatapointsLimit,
294 type: types.aggregation.avg.value 319 type: types.aggregation.avg.value
295 }; 320 };
296 } 321 }
@@ -298,7 +323,7 @@ function TimeService($translate, types) { @@ -298,7 +323,7 @@ function TimeService($translate, types) {
298 if (angular.isDefined(timewindow.aggregation) && !stateData) { 323 if (angular.isDefined(timewindow.aggregation) && !stateData) {
299 subscriptionTimewindow.aggregation = { 324 subscriptionTimewindow.aggregation = {
300 type: timewindow.aggregation.type || types.aggregation.avg.value, 325 type: timewindow.aggregation.type || types.aggregation.avg.value,
301 - limit: timewindow.aggregation.limit || AVG_LIMIT 326 + limit: timewindow.aggregation.limit || maxDatapointsLimit
302 }; 327 };
303 } 328 }
304 if (angular.isDefined(timewindow.realtime)) { 329 if (angular.isDefined(timewindow.realtime)) {
@@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin, @@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
22 .name; 22 .name;
23 23
24 /*@ngInject*/ 24 /*@ngInject*/
25 -function UserService($http, $q, $rootScope, adminService, dashboardService, loginService, toast, store, jwtHelper, $translate, $state, $location) { 25 +function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location) {
26 var currentUser = null, 26 var currentUser = null,
27 currentUserDetails = null, 27 currentUserDetails = null,
28 lastPublicDashboardId = null, 28 lastPublicDashboardId = null,
@@ -390,6 +390,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi @@ -390,6 +390,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
390 function loadSystemParams() { 390 function loadSystemParams() {
391 var promises = []; 391 var promises = [];
392 promises.push(loadIsUserTokenAccessEnabled()); 392 promises.push(loadIsUserTokenAccessEnabled());
  393 + promises.push(timeService.loadMaxDatapointsLimit());
393 return $q.all(promises); 394 return $q.all(promises);
394 } 395 }
395 396
@@ -31,6 +31,8 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic @@ -31,6 +31,8 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
31 vm.maxRealtimeAggInterval = maxRealtimeAggInterval; 31 vm.maxRealtimeAggInterval = maxRealtimeAggInterval;
32 vm.minHistoryAggInterval = minHistoryAggInterval; 32 vm.minHistoryAggInterval = minHistoryAggInterval;
33 vm.maxHistoryAggInterval = maxHistoryAggInterval; 33 vm.maxHistoryAggInterval = maxHistoryAggInterval;
  34 + vm.minDatapointsLimit = minDatapointsLimit;
  35 + vm.maxDatapointsLimit = maxDatapointsLimit;
34 36
35 if (vm.historyOnly) { 37 if (vm.historyOnly) {
36 vm.timewindow.selectedTab = 1; 38 vm.timewindow.selectedTab = 1;
@@ -86,6 +88,14 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic @@ -86,6 +88,14 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
86 return timeService.maxIntervalLimit(currentHistoryTimewindow()); 88 return timeService.maxIntervalLimit(currentHistoryTimewindow());
87 } 89 }
88 90
  91 + function minDatapointsLimit () {
  92 + return timeService.getMinDatapointsLimit();
  93 + }
  94 +
  95 + function maxDatapointsLimit () {
  96 + return timeService.getMaxDatapointsLimit();
  97 + }
  98 +
89 function currentHistoryTimewindow() { 99 function currentHistoryTimewindow() {
90 if (vm.timewindow.history.historyType === 0) { 100 if (vm.timewindow.history.historyType === 0) {
91 return vm.timewindow.history.timewindowMs; 101 return vm.timewindow.history.timewindowMs;
@@ -60,19 +60,22 @@ @@ -60,19 +60,22 @@
60 </md-option> 60 </md-option>
61 </md-select> 61 </md-select>
62 </md-input-container> 62 </md-input-container>
63 - <md-slider-container ng-show="vm.showLimit()"> 63 + <md-slider-container ng-if="vm.showLimit()">
64 <span translate>aggregation.limit</span> 64 <span translate>aggregation.limit</span>
65 - <md-slider flex min="10" max="500" ng-model="vm.timewindow.aggregation.limit" aria-label="limit" id="limit-slider"> 65 + <md-slider flex min="{{vm.minDatapointsLimit()}}" max="{{vm.maxDatapointsLimit()}}" step="1"
  66 + ng-model="vm.timewindow.aggregation.limit" aria-label="limit" id="limit-slider">
66 </md-slider> 67 </md-slider>
67 - <md-input-container>  
68 - <input flex type="number" ng-model="vm.timewindow.aggregation.limit" aria-label="limit" aria-controls="limit-slider"> 68 + <md-input-container style="max-width: 80px;">
  69 + <input flex type="number" step="1"
  70 + min="{{vm.minDatapointsLimit()}}" max="{{vm.maxDatapointsLimit()}}"
  71 + ng-model="vm.timewindow.aggregation.limit" aria-label="limit" aria-controls="limit-slider">
69 </md-input-container> 72 </md-input-container>
70 </md-slider-container> 73 </md-slider-container>
71 - <tb-timeinterval ng-show="vm.showRealtimeAggInterval()" min="vm.minRealtimeAggInterval()" max="vm.maxRealtimeAggInterval()" 74 + <tb-timeinterval ng-if="vm.showRealtimeAggInterval()" min="vm.minRealtimeAggInterval()" max="vm.maxRealtimeAggInterval()"
72 predefined-name="'aggregation.group-interval'" 75 predefined-name="'aggregation.group-interval'"
73 ng-model="vm.timewindow.realtime.interval"> 76 ng-model="vm.timewindow.realtime.interval">
74 </tb-timeinterval> 77 </tb-timeinterval>
75 - <tb-timeinterval ng-show="vm.showHistoryAggInterval()" min="vm.minHistoryAggInterval()" max="vm.maxHistoryAggInterval()" 78 + <tb-timeinterval ng-if="vm.showHistoryAggInterval()" min="vm.minHistoryAggInterval()" max="vm.maxHistoryAggInterval()"
76 predefined-name="'aggregation.group-interval'" 79 predefined-name="'aggregation.group-interval'"
77 ng-model="vm.timewindow.history.interval"> 80 ng-model="vm.timewindow.history.interval">
78 </tb-timeinterval> 81 </tb-timeinterval>
@@ -228,7 +228,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM @@ -228,7 +228,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
228 if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) { 228 if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) {
229 model.aggregation.type = value.aggregation.type; 229 model.aggregation.type = value.aggregation.type;
230 } 230 }
231 - model.aggregation.limit = value.aggregation.limit || timeService.avgAggregationLimit(); 231 + model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2);
232 } 232 }
233 } 233 }
234 scope.updateDisplayValue(); 234 scope.updateDisplayValue();