Commit 2f4df2803663f01df3bd5974cd7a16d2ce4710e1
1 parent
99dde0cf
Configurable max datapoints limit for Aggregation NONE
Showing
7 changed files
with
80 additions
and
22 deletions
... | ... | @@ -15,6 +15,8 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | +import lombok.Getter; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
18 | 20 | import org.springframework.http.HttpStatus; |
19 | 21 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | 22 | import org.springframework.web.bind.annotation.PathVariable; |
... | ... | @@ -49,6 +51,11 @@ public class DashboardController extends BaseController { |
49 | 51 | |
50 | 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 | 59 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
53 | 60 | @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET) |
54 | 61 | @ResponseBody |
... | ... | @@ -57,6 +64,13 @@ public class DashboardController extends BaseController { |
57 | 64 | } |
58 | 65 | |
59 | 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 | 74 | @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET) |
61 | 75 | @ResponseBody |
62 | 76 | public DashboardInfo getDashboardInfoById(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | ... | ... |
... | ... | @@ -77,6 +77,11 @@ security: |
77 | 77 | # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator |
78 | 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 | 85 | # Device communication protocol parameters |
81 | 86 | http: |
82 | 87 | request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" | ... | ... |
... | ... | @@ -26,15 +26,17 @@ const MIN_INTERVAL = SECOND; |
26 | 26 | const MAX_INTERVAL = 365 * 20 * DAY; |
27 | 27 | |
28 | 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 | 32 | /*@ngInject*/ |
33 | -function TimeService($translate, types) { | |
33 | +function TimeService($translate, $http, $q, types) { | |
34 | 34 | |
35 | 35 | var predefIntervals; |
36 | + var maxDatapointsLimit; | |
36 | 37 | |
37 | 38 | var service = { |
39 | + loadMaxDatapointsLimit: loadMaxDatapointsLimit, | |
38 | 40 | minIntervalLimit: minIntervalLimit, |
39 | 41 | maxIntervalLimit: maxIntervalLimit, |
40 | 42 | boundMinInterval: boundMinInterval, |
... | ... | @@ -45,20 +47,38 @@ function TimeService($translate, types) { |
45 | 47 | defaultTimewindow: defaultTimewindow, |
46 | 48 | toHistoryTimewindow: toHistoryTimewindow, |
47 | 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 | 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 | 75 | function minIntervalLimit(timewindow) { |
56 | - var min = timewindow / MAX_LIMIT; | |
76 | + var min = timewindow / 500; | |
57 | 77 | return boundMinInterval(min); |
58 | 78 | } |
59 | 79 | |
60 | 80 | function avgInterval(timewindow) { |
61 | - var avg = timewindow / AVG_LIMIT; | |
81 | + var avg = timewindow / 200; | |
62 | 82 | return boundMinInterval(avg); |
63 | 83 | } |
64 | 84 | |
... | ... | @@ -230,7 +250,7 @@ function TimeService($translate, types) { |
230 | 250 | }, |
231 | 251 | aggregation: { |
232 | 252 | type: types.aggregation.avg.value, |
233 | - limit: AVG_LIMIT | |
253 | + limit: Math.floor(maxDatapointsLimit / 2) | |
234 | 254 | } |
235 | 255 | } |
236 | 256 | return timewindow; |
... | ... | @@ -246,22 +266,27 @@ function TimeService($translate, types) { |
246 | 266 | } |
247 | 267 | |
248 | 268 | var aggType; |
269 | + var limit; | |
249 | 270 | if (timewindow.aggregation) { |
250 | 271 | aggType = timewindow.aggregation.type || types.aggregation.avg.value; |
272 | + limit = timewindow.aggregation.limit || maxDatapointsLimit; | |
251 | 273 | } else { |
252 | 274 | aggType = types.aggregation.avg.value; |
275 | + limit = maxDatapointsLimit; | |
253 | 276 | } |
254 | 277 | |
278 | + | |
255 | 279 | var historyTimewindow = { |
256 | 280 | history: { |
257 | 281 | fixedTimewindow: { |
258 | 282 | startTimeMs: startTimeMs, |
259 | 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 | 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 | 300 | realtimeWindowMs: null, |
276 | 301 | aggregation: { |
277 | 302 | interval: SECOND, |
278 | - limit: AVG_LIMIT, | |
303 | + limit: maxDatapointsLimit, | |
279 | 304 | type: types.aggregation.avg.value |
280 | 305 | } |
281 | 306 | }; |
... | ... | @@ -283,14 +308,14 @@ function TimeService($translate, types) { |
283 | 308 | if (stateData) { |
284 | 309 | subscriptionTimewindow.aggregation = { |
285 | 310 | interval: SECOND, |
286 | - limit: MAX_LIMIT, | |
311 | + limit: maxDatapointsLimit, | |
287 | 312 | type: types.aggregation.none.value, |
288 | 313 | stateData: true |
289 | 314 | }; |
290 | 315 | } else { |
291 | 316 | subscriptionTimewindow.aggregation = { |
292 | 317 | interval: SECOND, |
293 | - limit: AVG_LIMIT, | |
318 | + limit: maxDatapointsLimit, | |
294 | 319 | type: types.aggregation.avg.value |
295 | 320 | }; |
296 | 321 | } |
... | ... | @@ -298,7 +323,7 @@ function TimeService($translate, types) { |
298 | 323 | if (angular.isDefined(timewindow.aggregation) && !stateData) { |
299 | 324 | subscriptionTimewindow.aggregation = { |
300 | 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 | 329 | if (angular.isDefined(timewindow.realtime)) { | ... | ... |
... | ... | @@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin, |
22 | 22 | .name; |
23 | 23 | |
24 | 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 | 26 | var currentUser = null, |
27 | 27 | currentUserDetails = null, |
28 | 28 | lastPublicDashboardId = null, |
... | ... | @@ -390,6 +390,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi |
390 | 390 | function loadSystemParams() { |
391 | 391 | var promises = []; |
392 | 392 | promises.push(loadIsUserTokenAccessEnabled()); |
393 | + promises.push(timeService.loadMaxDatapointsLimit()); | |
393 | 394 | return $q.all(promises); |
394 | 395 | } |
395 | 396 | ... | ... |
... | ... | @@ -31,6 +31,8 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic |
31 | 31 | vm.maxRealtimeAggInterval = maxRealtimeAggInterval; |
32 | 32 | vm.minHistoryAggInterval = minHistoryAggInterval; |
33 | 33 | vm.maxHistoryAggInterval = maxHistoryAggInterval; |
34 | + vm.minDatapointsLimit = minDatapointsLimit; | |
35 | + vm.maxDatapointsLimit = maxDatapointsLimit; | |
34 | 36 | |
35 | 37 | if (vm.historyOnly) { |
36 | 38 | vm.timewindow.selectedTab = 1; |
... | ... | @@ -86,6 +88,14 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic |
86 | 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 | 99 | function currentHistoryTimewindow() { |
90 | 100 | if (vm.timewindow.history.historyType === 0) { |
91 | 101 | return vm.timewindow.history.timewindowMs; | ... | ... |
... | ... | @@ -60,19 +60,22 @@ |
60 | 60 | </md-option> |
61 | 61 | </md-select> |
62 | 62 | </md-input-container> |
63 | - <md-slider-container ng-show="vm.showLimit()"> | |
63 | + <md-slider-container ng-if="vm.showLimit()"> | |
64 | 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 | 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 | 72 | </md-input-container> |
70 | 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 | 75 | predefined-name="'aggregation.group-interval'" |
73 | 76 | ng-model="vm.timewindow.realtime.interval"> |
74 | 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 | 79 | predefined-name="'aggregation.group-interval'" |
77 | 80 | ng-model="vm.timewindow.history.interval"> |
78 | 81 | </tb-timeinterval> | ... | ... |
... | ... | @@ -228,7 +228,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM |
228 | 228 | if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) { |
229 | 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 | 234 | scope.updateDisplayValue(); | ... | ... |