2019-08-09 19:13:18 +03:00
|
|
|
///
|
2020-02-20 10:26:43 +02:00
|
|
|
/// Copyright © 2016-2020 The Thingsboard Authors
|
2019-08-09 19:13:18 +03:00
|
|
|
///
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
|
|
|
|
|
import { Injectable } from '@angular/core';
|
2020-04-27 09:27:14 +03:00
|
|
|
import {
|
|
|
|
|
AggregationType,
|
|
|
|
|
DAY,
|
|
|
|
|
defaultTimeIntervals,
|
|
|
|
|
defaultTimewindow,
|
|
|
|
|
SECOND,
|
|
|
|
|
Timewindow
|
|
|
|
|
} from '@shared/models/time/time.models';
|
2019-09-10 15:12:10 +03:00
|
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
|
|
import { Observable } from 'rxjs';
|
|
|
|
|
import { defaultHttpOptions } from '@core/http/http-utils';
|
|
|
|
|
import { map } from 'rxjs/operators';
|
2020-02-18 17:25:17 +02:00
|
|
|
import { isDefined } from '@core/utils';
|
2019-08-09 19:13:18 +03:00
|
|
|
|
|
|
|
|
export interface TimeInterval {
|
|
|
|
|
name: string;
|
|
|
|
|
translateParams: {[key: string]: any};
|
|
|
|
|
value: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MIN_INTERVAL = SECOND;
|
|
|
|
|
const MAX_INTERVAL = 365 * 20 * DAY;
|
|
|
|
|
|
2020-06-26 16:31:17 +03:00
|
|
|
const MIN_LIMIT = 7;
|
2019-08-09 19:13:18 +03:00
|
|
|
|
|
|
|
|
const MAX_DATAPOINTS_LIMIT = 500;
|
|
|
|
|
|
|
|
|
|
@Injectable({
|
|
|
|
|
providedIn: 'root'
|
|
|
|
|
})
|
|
|
|
|
export class TimeService {
|
|
|
|
|
|
|
|
|
|
private maxDatapointsLimit = MAX_DATAPOINTS_LIMIT;
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
private http: HttpClient
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
public loadMaxDatapointsLimit(): Observable<number> {
|
|
|
|
|
return this.http.get<number>('/api/dashboard/maxDatapointsLimit',
|
|
|
|
|
defaultHttpOptions(true)).pipe(
|
|
|
|
|
map( (limit) => {
|
|
|
|
|
this.maxDatapointsLimit = limit;
|
|
|
|
|
if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) {
|
|
|
|
|
this.maxDatapointsLimit = MIN_LIMIT + 1;
|
|
|
|
|
}
|
|
|
|
|
return this.maxDatapointsLimit;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public matchesExistingInterval(min: number, max: number, intervalMs: number): boolean {
|
|
|
|
|
const intervals = this.getIntervals(min, max);
|
|
|
|
|
return intervals.findIndex(interval => interval.value === intervalMs) > -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getIntervals(min: number, max: number): Array<TimeInterval> {
|
|
|
|
|
min = this.boundMinInterval(min);
|
|
|
|
|
max = this.boundMaxInterval(max);
|
|
|
|
|
return defaultTimeIntervals.filter((interval) => interval.value >= min && interval.value <= max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boundMinInterval(min: number): number {
|
2020-02-18 17:25:17 +02:00
|
|
|
if (isDefined(min)) {
|
|
|
|
|
min = Math.floor(min / 1000) * 1000;
|
|
|
|
|
}
|
2019-08-09 19:13:18 +03:00
|
|
|
return this.toBound(min, MIN_INTERVAL, MAX_INTERVAL, MIN_INTERVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boundMaxInterval(max: number): number {
|
2020-02-18 17:25:17 +02:00
|
|
|
if (isDefined(max)) {
|
|
|
|
|
max = Math.floor(max / 1000) * 1000;
|
|
|
|
|
}
|
2019-08-09 19:13:18 +03:00
|
|
|
return this.toBound(max, MIN_INTERVAL, MAX_INTERVAL, MAX_INTERVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boundToPredefinedInterval(min: number, max: number, intervalMs: number): number {
|
|
|
|
|
const intervals = this.getIntervals(min, max);
|
|
|
|
|
let minDelta = MAX_INTERVAL;
|
|
|
|
|
const boundedInterval = intervalMs || min;
|
2019-09-03 19:31:16 +03:00
|
|
|
if (!intervals.length) {
|
|
|
|
|
return boundedInterval;
|
|
|
|
|
}
|
2019-08-09 19:13:18 +03:00
|
|
|
let matchedInterval: TimeInterval = intervals[0];
|
|
|
|
|
intervals.forEach((interval) => {
|
|
|
|
|
const delta = Math.abs(interval.value - boundedInterval);
|
|
|
|
|
if (delta < minDelta) {
|
|
|
|
|
matchedInterval = interval;
|
|
|
|
|
minDelta = delta;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return matchedInterval.value;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-10 15:12:10 +03:00
|
|
|
public boundIntervalToTimewindow(timewindow: number, intervalMs: number, aggType: AggregationType): number {
|
|
|
|
|
if (aggType === AggregationType.NONE) {
|
|
|
|
|
return SECOND;
|
|
|
|
|
} else {
|
|
|
|
|
const min = this.minIntervalLimit(timewindow);
|
|
|
|
|
const max = this.maxIntervalLimit(timewindow);
|
|
|
|
|
if (intervalMs) {
|
|
|
|
|
return this.toBound(intervalMs, min, max, intervalMs);
|
|
|
|
|
} else {
|
|
|
|
|
return this.boundToPredefinedInterval(min, max, this.avgInterval(timewindow));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 19:13:18 +03:00
|
|
|
public getMaxDatapointsLimit(): number {
|
|
|
|
|
return this.maxDatapointsLimit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getMinDatapointsLimit(): number {
|
|
|
|
|
return MIN_LIMIT;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-10 15:12:10 +03:00
|
|
|
public avgInterval(timewindow: number): number {
|
|
|
|
|
const avg = timewindow / 200;
|
|
|
|
|
return this.boundMinInterval(avg);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 19:13:18 +03:00
|
|
|
public minIntervalLimit(timewindowMs: number): number {
|
|
|
|
|
const min = timewindowMs / 500;
|
|
|
|
|
return this.boundMinInterval(min);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public maxIntervalLimit(timewindowMs: number): number {
|
|
|
|
|
const max = timewindowMs / MIN_LIMIT;
|
|
|
|
|
return this.boundMaxInterval(max);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 19:31:16 +03:00
|
|
|
public defaultTimewindow(): Timewindow {
|
2019-09-10 15:12:10 +03:00
|
|
|
return defaultTimewindow(this);
|
2019-09-03 19:31:16 +03:00
|
|
|
}
|
|
|
|
|
|
2019-08-09 19:13:18 +03:00
|
|
|
private toBound(value: number, min: number, max: number, defValue: number): number {
|
2020-02-18 17:25:17 +02:00
|
|
|
if (isDefined(value)) {
|
2019-08-09 19:13:18 +03:00
|
|
|
value = Math.max(value, min);
|
|
|
|
|
value = Math.min(value, max);
|
|
|
|
|
return value;
|
|
|
|
|
} else {
|
|
|
|
|
return defValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|