Configurable max datapoints limit for Aggregation NONE

This commit is contained in:
Igor Kulikov 2018-10-08 20:14:49 +03:00
parent 99dde0cf69
commit 2f4df28036
7 changed files with 80 additions and 22 deletions

View File

@ -15,6 +15,8 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -49,6 +51,11 @@ public class DashboardController extends BaseController {
public static final String DASHBOARD_ID = "dashboardId"; public static final String DASHBOARD_ID = "dashboardId";
@Value("${dashboard.max_datapoints_limit}")
@Getter
private long maxDatapointsLimit;
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET) @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET)
@ResponseBody @ResponseBody
@ -56,6 +63,13 @@ public class DashboardController extends BaseController {
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
@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')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET) @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
@ResponseBody @ResponseBody

View File

@ -77,6 +77,11 @@ security:
# Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator # 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}" 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 # Device communication protocol parameters
http: http:
request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"

View File

@ -26,15 +26,17 @@ const MIN_INTERVAL = SECOND;
const MAX_INTERVAL = 365 * 20 * DAY; const MAX_INTERVAL = 365 * 20 * DAY;
const MIN_LIMIT = 10; const MIN_LIMIT = 10;
const AVG_LIMIT = 200; //const AVG_LIMIT = 200;
const MAX_LIMIT = 500; //const MAX_LIMIT = 500;
/*@ngInject*/ /*@ngInject*/
function TimeService($translate, types) { function TimeService($translate, $http, $q, types) {
var predefIntervals; var predefIntervals;
var maxDatapointsLimit;
var service = { var service = {
loadMaxDatapointsLimit: loadMaxDatapointsLimit,
minIntervalLimit: minIntervalLimit, minIntervalLimit: minIntervalLimit,
maxIntervalLimit: maxIntervalLimit, maxIntervalLimit: maxIntervalLimit,
boundMinInterval: boundMinInterval, boundMinInterval: boundMinInterval,
@ -45,20 +47,38 @@ function TimeService($translate, types) {
defaultTimewindow: defaultTimewindow, defaultTimewindow: defaultTimewindow,
toHistoryTimewindow: toHistoryTimewindow, toHistoryTimewindow: toHistoryTimewindow,
createSubscriptionTimewindow: createSubscriptionTimewindow, createSubscriptionTimewindow: createSubscriptionTimewindow,
avgAggregationLimit: function () { getMaxDatapointsLimit: function () {
return AVG_LIMIT; return maxDatapointsLimit;
},
getMinDatapointsLimit: function () {
return MIN_LIMIT;
} }
} }
return service; 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) { function minIntervalLimit(timewindow) {
var min = timewindow / MAX_LIMIT; var min = timewindow / 500;
return boundMinInterval(min); return boundMinInterval(min);
} }
function avgInterval(timewindow) { function avgInterval(timewindow) {
var avg = timewindow / AVG_LIMIT; var avg = timewindow / 200;
return boundMinInterval(avg); return boundMinInterval(avg);
} }
@ -230,7 +250,7 @@ function TimeService($translate, types) {
}, },
aggregation: { aggregation: {
type: types.aggregation.avg.value, type: types.aggregation.avg.value,
limit: AVG_LIMIT limit: Math.floor(maxDatapointsLimit / 2)
} }
} }
return timewindow; return timewindow;
@ -246,22 +266,27 @@ function TimeService($translate, types) {
} }
var aggType; var aggType;
var limit;
if (timewindow.aggregation) { if (timewindow.aggregation) {
aggType = timewindow.aggregation.type || types.aggregation.avg.value; aggType = timewindow.aggregation.type || types.aggregation.avg.value;
limit = timewindow.aggregation.limit || maxDatapointsLimit;
} else { } else {
aggType = types.aggregation.avg.value; aggType = types.aggregation.avg.value;
limit = maxDatapointsLimit;
} }
var historyTimewindow = { var historyTimewindow = {
history: { history: {
fixedTimewindow: { fixedTimewindow: {
startTimeMs: startTimeMs, startTimeMs: startTimeMs,
endTimeMs: endTimeMs endTimeMs: endTimeMs
}, },
interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, aggType) interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, types.aggregation.avg.value)
}, },
aggregation: { aggregation: {
type: aggType type: aggType,
limit: limit
} }
} }
@ -275,7 +300,7 @@ function TimeService($translate, types) {
realtimeWindowMs: null, realtimeWindowMs: null,
aggregation: { aggregation: {
interval: SECOND, interval: SECOND,
limit: AVG_LIMIT, limit: maxDatapointsLimit,
type: types.aggregation.avg.value type: types.aggregation.avg.value
} }
}; };
@ -283,14 +308,14 @@ function TimeService($translate, types) {
if (stateData) { if (stateData) {
subscriptionTimewindow.aggregation = { subscriptionTimewindow.aggregation = {
interval: SECOND, interval: SECOND,
limit: MAX_LIMIT, limit: maxDatapointsLimit,
type: types.aggregation.none.value, type: types.aggregation.none.value,
stateData: true stateData: true
}; };
} else { } else {
subscriptionTimewindow.aggregation = { subscriptionTimewindow.aggregation = {
interval: SECOND, interval: SECOND,
limit: AVG_LIMIT, limit: maxDatapointsLimit,
type: types.aggregation.avg.value type: types.aggregation.avg.value
}; };
} }
@ -298,7 +323,7 @@ function TimeService($translate, types) {
if (angular.isDefined(timewindow.aggregation) && !stateData) { if (angular.isDefined(timewindow.aggregation) && !stateData) {
subscriptionTimewindow.aggregation = { subscriptionTimewindow.aggregation = {
type: timewindow.aggregation.type || types.aggregation.avg.value, type: timewindow.aggregation.type || types.aggregation.avg.value,
limit: timewindow.aggregation.limit || AVG_LIMIT limit: timewindow.aggregation.limit || maxDatapointsLimit
}; };
} }
if (angular.isDefined(timewindow.realtime)) { if (angular.isDefined(timewindow.realtime)) {

View File

@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
.name; .name;
/*@ngInject*/ /*@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, var currentUser = null,
currentUserDetails = null, currentUserDetails = null,
lastPublicDashboardId = null, lastPublicDashboardId = null,
@ -390,6 +390,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
function loadSystemParams() { function loadSystemParams() {
var promises = []; var promises = [];
promises.push(loadIsUserTokenAccessEnabled()); promises.push(loadIsUserTokenAccessEnabled());
promises.push(timeService.loadMaxDatapointsLimit());
return $q.all(promises); return $q.all(promises);
} }

View File

@ -31,6 +31,8 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
vm.maxRealtimeAggInterval = maxRealtimeAggInterval; vm.maxRealtimeAggInterval = maxRealtimeAggInterval;
vm.minHistoryAggInterval = minHistoryAggInterval; vm.minHistoryAggInterval = minHistoryAggInterval;
vm.maxHistoryAggInterval = maxHistoryAggInterval; vm.maxHistoryAggInterval = maxHistoryAggInterval;
vm.minDatapointsLimit = minDatapointsLimit;
vm.maxDatapointsLimit = maxDatapointsLimit;
if (vm.historyOnly) { if (vm.historyOnly) {
vm.timewindow.selectedTab = 1; vm.timewindow.selectedTab = 1;
@ -86,6 +88,14 @@ export default function TimewindowPanelController(mdPanelRef, $scope, timeServic
return timeService.maxIntervalLimit(currentHistoryTimewindow()); return timeService.maxIntervalLimit(currentHistoryTimewindow());
} }
function minDatapointsLimit () {
return timeService.getMinDatapointsLimit();
}
function maxDatapointsLimit () {
return timeService.getMaxDatapointsLimit();
}
function currentHistoryTimewindow() { function currentHistoryTimewindow() {
if (vm.timewindow.history.historyType === 0) { if (vm.timewindow.history.historyType === 0) {
return vm.timewindow.history.timewindowMs; return vm.timewindow.history.timewindowMs;

View File

@ -60,19 +60,22 @@
</md-option> </md-option>
</md-select> </md-select>
</md-input-container> </md-input-container>
<md-slider-container ng-show="vm.showLimit()"> <md-slider-container ng-if="vm.showLimit()">
<span translate>aggregation.limit</span> <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-slider>
<md-input-container> <md-input-container style="max-width: 80px;">
<input flex type="number" ng-model="vm.timewindow.aggregation.limit" aria-label="limit" aria-controls="limit-slider"> <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-input-container>
</md-slider-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'" predefined-name="'aggregation.group-interval'"
ng-model="vm.timewindow.realtime.interval"> ng-model="vm.timewindow.realtime.interval">
</tb-timeinterval> </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'" predefined-name="'aggregation.group-interval'"
ng-model="vm.timewindow.history.interval"> ng-model="vm.timewindow.history.interval">
</tb-timeinterval> </tb-timeinterval>

View File

@ -228,7 +228,7 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) { if (angular.isDefined(value.aggregation.type) && value.aggregation.type.length > 0) {
model.aggregation.type = value.aggregation.type; 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(); scope.updateDisplayValue();