Commit d184d8db7298d2f224cb5d6a315eab29c6e39211

Authored by Igor Kulikov
2 parents 59f85f1c 8f8613db

Merge branch 'feature/timeWindow/quick-interval' of https://github.com/vvlladd28…

…/thingsboard into vvlladd28-feature/timeWindow/quick-interval
... ... @@ -15,7 +15,12 @@
15 15 ///
16 16
17 17 import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models';
18   -import { AggregationType } from '@shared/models/time/time.models';
  18 +import {
  19 + AggregationType,
  20 + calculateIntervalEndTime,
  21 + calculateIntervalStartTime,
  22 + QuickTimeInterval
  23 +} from '@shared/models/time/time.models';
19 24 import { UtilsService } from '@core/services/utils.service';
20 25 import { deepClone } from '@core/utils';
21 26 import Timeout = NodeJS.Timeout;
... ... @@ -92,7 +97,8 @@ export class DataAggregator {
92 97 private interval: number,
93 98 private stateData: boolean,
94 99 private utils: UtilsService,
95   - private ignoreDataUpdateOnIntervalTick: boolean) {
  100 + private ignoreDataUpdateOnIntervalTick: boolean,
  101 + private quickInterval: QuickTimeInterval) {
96 102 this.tsKeyNames.forEach((key) => {
97 103 this.dataBuffer[key] = [];
98 104 });
... ... @@ -138,7 +144,8 @@ export class DataAggregator {
138 144 this.startTs = startTs;
139 145 this.timeWindow = timeWindow;
140 146 this.interval = interval;
141   - this.endTs = this.startTs + this.timeWindow;
  147 + const endTs = this.startTs + this.timeWindow;
  148 + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs);
142 149 this.elapsed = 0;
143 150 this.aggregationTimeout = Math.max(this.interval, 1000);
144 151 this.resetPending = true;
... ... @@ -161,7 +168,8 @@ export class DataAggregator {
161 168 if (!this.dataReceived) {
162 169 this.elapsed = 0;
163 170 this.dataReceived = true;
164   - this.endTs = this.startTs + this.timeWindow;
  171 + const endTs = this.startTs + this.timeWindow;
  172 + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs);
165 173 }
166 174 if (this.resetPending) {
167 175 this.resetPending = false;
... ... @@ -197,8 +205,11 @@ export class DataAggregator {
197 205 if (!history) {
198 206 const delta = Math.floor(this.elapsed / this.interval);
199 207 if (delta || !this.data) {
200   - this.startTs += delta * this.interval;
201   - this.endTs += delta * this.interval;
  208 + const tickTs = delta * this.interval;
  209 + const startTS = this.startTs + tickTs;
  210 + this.startTs = calculateIntervalStartTime(this.quickInterval, startTS);
  211 + const endTs = this.endTs + tickTs;
  212 + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs);
202 213 this.data = this.updateData();
203 214 this.elapsed = this.elapsed - delta * this.interval;
204 215 }
... ...
... ... @@ -752,7 +752,8 @@ export class EntityDataSubscription {
752 752 subsTw.aggregation.interval,
753 753 subsTw.aggregation.stateData,
754 754 this.utils,
755   - this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick
  755 + this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick,
  756 + subsTw.quickInterval
756 757 );
757 758 }
758 759
... ...
... ... @@ -37,6 +37,8 @@ import {
37 37 } from '@app/shared/models/widget.models';
38 38 import { HttpErrorResponse } from '@angular/common/http';
39 39 import {
  40 + calculateIntervalEndTime,
  41 + calculateIntervalStartTime,
40 42 createSubscriptionTimewindow,
41 43 createTimewindowForComparison,
42 44 SubscriptionTimewindow,
... ... @@ -1081,8 +1083,10 @@ export class WidgetSubscription implements IWidgetSubscription {
1081 1083 private updateTimewindow() {
1082 1084 this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000;
1083 1085 if (this.subscriptionTimewindow.realtimeWindowMs) {
1084   - this.timeWindow.maxTime = moment().valueOf() + this.timeWindow.stDiff;
1085   - this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs;
  1086 + this.timeWindow.maxTime = calculateIntervalEndTime(
  1087 + this.subscriptionTimewindow.quickInterval, moment().valueOf() + this.timeWindow.stDiff);
  1088 + this.timeWindow.minTime = calculateIntervalStartTime(
  1089 + this.subscriptionTimewindow.quickInterval, this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs);
1086 1090 } else if (this.subscriptionTimewindow.fixedWindow) {
1087 1091 this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs;
1088 1092 this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs;
... ... @@ -1105,7 +1109,7 @@ export class WidgetSubscription implements IWidgetSubscription {
1105 1109 this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000;
1106 1110 if (this.timewindowForComparison.realtimeWindowMs) {
1107 1111 this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf();
1108   - this.comparisonTimeWindow.minTime = this.comparisonTimeWindow.maxTime - this.timewindowForComparison.realtimeWindowMs;
  1112 + this.comparisonTimeWindow.minTime = moment(this.timeWindow.minTime).subtract(1, this.timeForComparison).valueOf();
1109 1113 } else if (this.timewindowForComparison.fixedWindow) {
1110 1114 this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs;
1111 1115 this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs;
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<section class="interval-section" fxLayout="row" fxFlex>
  19 + <mat-form-field fxFlex>
  20 + <mat-label translate>timewindow.interval</mat-label>
  21 + <mat-select [disabled]="disabled" [(ngModel)]="modelValue" (ngModelChange)="onIntervalChange()">
  22 + <mat-option *ngFor="let interval of intervals" [value]="interval">
  23 + {{ timeIntervalTranslationMap.get(interval) | translate}}
  24 + </mat-option>
  25 + </mat-select>
  26 + </mat-form-field>
  27 +</section>
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +:host {
  18 + min-width: 364px;
  19 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, forwardRef, Input, OnInit } from '@angular/core';
  18 +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
  19 +import { QuickTimeInterval, QuickTimeIntervalTranslationMap } from '@shared/models/time/time.models';
  20 +
  21 +@Component({
  22 + selector: 'tb-quick-time-interval',
  23 + templateUrl: './quick-time-interval.component.html',
  24 + styleUrls: ['./quick-time-interval.component.scss'],
  25 + providers: [
  26 + {
  27 + provide: NG_VALUE_ACCESSOR,
  28 + useExisting: forwardRef(() => QuickTimeIntervalComponent),
  29 + multi: true
  30 + }
  31 + ]
  32 +})
  33 +export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor {
  34 +
  35 + private allIntervals = Object.values(QuickTimeInterval);
  36 +
  37 + modelValue: QuickTimeInterval;
  38 + timeIntervalTranslationMap = QuickTimeIntervalTranslationMap;
  39 +
  40 + rendered = false;
  41 +
  42 + @Input() disabled: boolean;
  43 +
  44 + @Input() onlyCurrentInterval = false;
  45 +
  46 + private propagateChange = (_: any) => {};
  47 +
  48 + constructor() {
  49 + }
  50 +
  51 + get intervals() {
  52 + if (this.onlyCurrentInterval) {
  53 + return this.allIntervals.filter(interval => interval.startsWith('CURRENT_'));
  54 + }
  55 + return this.allIntervals;
  56 + }
  57 +
  58 + ngOnInit(): void {
  59 + }
  60 +
  61 + registerOnChange(fn: any): void {
  62 + this.propagateChange = fn;
  63 + }
  64 +
  65 + registerOnTouched(fn: any): void {
  66 + }
  67 +
  68 + setDisabledState(isDisabled: boolean): void {
  69 + this.disabled = isDisabled;
  70 + }
  71 +
  72 + writeValue(interval: QuickTimeInterval): void {
  73 + this.modelValue = interval;
  74 + }
  75 +
  76 + onIntervalChange() {
  77 + this.propagateChange(this.modelValue);
  78 + }
  79 +}
... ...
... ... @@ -21,16 +21,43 @@
21 21 <mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}"
22 22 (selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab">
23 23 <mat-tab label="{{ 'timewindow.realtime' | translate }}">
24   - <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column">
25   - <tb-timeinterval
26   - [(hideFlag)]="timewindow.hideInterval"
27   - (hideFlagChange)="onHideIntervalChanged()"
28   - [isEdit]="isEdit"
29   - formControlName="timewindowMs"
30   - predefinedName="timewindow.last"
31   - [required]="timewindow.selectedTab === timewindowTypes.REALTIME"
32   - style="padding-top: 8px;"></tb-timeinterval>
33   - </div>
  24 + <section fxLayout="row">
  25 + <section *ngIf="isEdit" fxLayout="column" style="padding-top: 8px; padding-left: 16px;">
  26 + <label class="tb-small hide-label" translate>timewindow.hide</label>
  27 + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideInterval"
  28 + (ngModelChange)="onHideIntervalChanged()"></mat-checkbox>
  29 + </section>
  30 + <section fxLayout="column" fxFlex [fxShow]="isEdit || !timewindow.hideInterval">
  31 + <div formGroupName="realtime" class="mat-content mat-padding" style="padding-top: 8px;">
  32 + <mat-radio-group formControlName="realtimeType">
  33 + <mat-radio-button [value]="realtimeTypes.LAST_INTERVAL" color="primary">
  34 + <section fxLayout="column">
  35 + <span translate>timewindow.last</span>
  36 + <tb-timeinterval
  37 + formControlName="timewindowMs"
  38 + predefinedName="timewindow.last"
  39 + [fxShow]="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL"
  40 + [required]="timewindow.selectedTab === timewindowTypes.REALTIME &&
  41 + timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL"
  42 + style="padding-top: 8px;"></tb-timeinterval>
  43 + </section>
  44 + </mat-radio-button>
  45 + <mat-radio-button [value]="realtimeTypes.INTERVAL" color="primary">
  46 + <section fxLayout="column">
  47 + <span translate>timewindow.interval</span>
  48 + <tb-quick-time-interval
  49 + formControlName="quickInterval"
  50 + onlyCurrentInterval="true"
  51 + [fxShow]="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL"
  52 + [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
  53 + timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL"
  54 + style="padding-top: 8px; min-width: 364px"></tb-quick-time-interval>
  55 + </section>
  56 + </mat-radio-button>
  57 + </mat-radio-group>
  58 + </div>
  59 + </section>
  60 + </section>
34 61 </mat-tab>
35 62 <mat-tab label="{{ 'timewindow.history' | translate }}">
36 63 <section fxLayout="row">
... ... @@ -65,6 +92,17 @@
65 92 style="padding-top: 8px;"></tb-datetime-period>
66 93 </section>
67 94 </mat-radio-button>
  95 + <mat-radio-button [value]="historyTypes.INTERVAL" color="primary">
  96 + <section fxLayout="column">
  97 + <span translate>timewindow.interval</span>
  98 + <tb-quick-time-interval
  99 + formControlName="quickInterval"
  100 + [fxShow]="timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"
  101 + [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
  102 + timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"
  103 + style="padding-top: 8px; min-width: 364px"></tb-quick-time-interval>
  104 + </section>
  105 + </mat-radio-button>
68 106 </mat-radio-group>
69 107 </div>
70 108 </section>
... ...
... ... @@ -20,6 +20,8 @@ import {
20 20 AggregationType,
21 21 DAY,
22 22 HistoryWindowType,
  23 + quickTimeIntervalPeriod,
  24 + RealtimeWindowType,
23 25 Timewindow,
24 26 TimewindowType
25 27 } from '@shared/models/time/time.models';
... ... @@ -60,6 +62,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
60 62
61 63 historyTypes = HistoryWindowType;
62 64
  65 + realtimeTypes = RealtimeWindowType;
  66 +
63 67 timewindowTypes = TimewindowType;
64 68
65 69 aggregationTypes = AggregationType;
... ... @@ -89,6 +93,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
89 93 this.timewindowForm = this.fb.group({
90 94 realtime: this.fb.group(
91 95 {
  96 + realtimeType: this.fb.control({
  97 + value: this.timewindow.realtime && typeof this.timewindow.realtime.realtimeType !== 'undefined'
  98 + ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL,
  99 + disabled: hideInterval
  100 + }),
92 101 timewindowMs: [
93 102 this.timewindow.realtime && typeof this.timewindow.realtime.timewindowMs !== 'undefined'
94 103 ? this.timewindow.realtime.timewindowMs : null
... ... @@ -96,7 +105,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
96 105 interval: [
97 106 this.timewindow.realtime && typeof this.timewindow.realtime.interval !== 'undefined'
98 107 ? this.timewindow.realtime.interval : null
99   - ]
  108 + ],
  109 + quickInterval: this.fb.control({
  110 + value: this.timewindow.realtime && typeof this.timewindow.realtime.quickInterval !== 'undefined'
  111 + ? this.timewindow.realtime.quickInterval : null,
  112 + disabled: hideInterval
  113 + })
100 114 }
101 115 ),
102 116 history: this.fb.group(
... ... @@ -119,6 +133,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
119 133 value: this.timewindow.history && typeof this.timewindow.history.fixedTimewindow !== 'undefined'
120 134 ? this.timewindow.history.fixedTimewindow : null,
121 135 disabled: hideInterval
  136 + }),
  137 + quickInterval: this.fb.control({
  138 + value: this.timewindow.history && typeof this.timewindow.history.quickInterval !== 'undefined'
  139 + ? this.timewindow.history.quickInterval : null,
  140 + disabled: hideInterval
122 141 })
123 142 }
124 143 ),
... ... @@ -142,14 +161,17 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
142 161 update() {
143 162 const timewindowFormValue = this.timewindowForm.getRawValue();
144 163 this.timewindow.realtime = {
  164 + realtimeType: timewindowFormValue.realtime.realtimeType,
145 165 timewindowMs: timewindowFormValue.realtime.timewindowMs,
  166 + quickInterval: timewindowFormValue.realtime.quickInterval,
146 167 interval: timewindowFormValue.realtime.interval
147 168 };
148 169 this.timewindow.history = {
149 170 historyType: timewindowFormValue.history.historyType,
150 171 timewindowMs: timewindowFormValue.history.timewindowMs,
151 172 interval: timewindowFormValue.history.interval,
152   - fixedTimewindow: timewindowFormValue.history.fixedTimewindow
  173 + fixedTimewindow: timewindowFormValue.history.fixedTimewindow,
  174 + quickInterval: timewindowFormValue.history.quickInterval
153 175 };
154 176 if (this.aggregation) {
155 177 this.timewindow.aggregation = {
... ... @@ -174,11 +196,23 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
174 196 }
175 197
176 198 minRealtimeAggInterval() {
177   - return this.timeService.minIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value);
  199 + return this.timeService.minIntervalLimit(this.currentRealtimeTimewindow());
178 200 }
179 201
180 202 maxRealtimeAggInterval() {
181   - return this.timeService.maxIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value);
  203 + return this.timeService.maxIntervalLimit(this.currentRealtimeTimewindow());
  204 + }
  205 +
  206 + currentRealtimeTimewindow(): number {
  207 + const timeWindowFormValue = this.timewindowForm.getRawValue();
  208 + switch (timeWindowFormValue.realtime.realtimeType) {
  209 + case RealtimeWindowType.LAST_INTERVAL:
  210 + return timeWindowFormValue.realtime.timewindowMs;
  211 + case RealtimeWindowType.INTERVAL:
  212 + return quickTimeIntervalPeriod(timeWindowFormValue.realtime.quickInterval);
  213 + default:
  214 + return DAY;
  215 + }
182 216 }
183 217
184 218 minHistoryAggInterval() {
... ... @@ -193,6 +227,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
193 227 const timewindowFormValue = this.timewindowForm.getRawValue();
194 228 if (timewindowFormValue.history.historyType === HistoryWindowType.LAST_INTERVAL) {
195 229 return timewindowFormValue.history.timewindowMs;
  230 + } else if (timewindowFormValue.history.historyType === HistoryWindowType.INTERVAL) {
  231 + return quickTimeIntervalPeriod(timewindowFormValue.history.quickInterval);
196 232 } else if (timewindowFormValue.history.fixedTimewindow) {
197 233 return timewindowFormValue.history.fixedTimewindow.endTimeMs -
198 234 timewindowFormValue.history.fixedTimewindow.startTimeMs;
... ... @@ -206,10 +242,18 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
206 242 this.timewindowForm.get('history.historyType').disable({emitEvent: false});
207 243 this.timewindowForm.get('history.timewindowMs').disable({emitEvent: false});
208 244 this.timewindowForm.get('history.fixedTimewindow').disable({emitEvent: false});
  245 + this.timewindowForm.get('history.quickInterval').disable({emitEvent: false});
  246 + this.timewindowForm.get('realtime.realtimeType').disable({emitEvent: false});
  247 + this.timewindowForm.get('realtime.timewindowMs').disable({emitEvent: false});
  248 + this.timewindowForm.get('realtime.quickInterval').disable({emitEvent: false});
209 249 } else {
210 250 this.timewindowForm.get('history.historyType').enable({emitEvent: false});
211 251 this.timewindowForm.get('history.timewindowMs').enable({emitEvent: false});
212 252 this.timewindowForm.get('history.fixedTimewindow').enable({emitEvent: false});
  253 + this.timewindowForm.get('history.quickInterval').enable({emitEvent: false});
  254 + this.timewindowForm.get('realtime.realtimeType').enable({emitEvent: false});
  255 + this.timewindowForm.get('realtime.timewindowMs').enable({emitEvent: false});
  256 + this.timewindowForm.get('realtime.quickInterval').enable({emitEvent: false});
213 257 }
214 258 this.timewindowForm.markAsDirty();
215 259 }
... ...
... ... @@ -33,6 +33,8 @@ import {
33 33 cloneSelectedTimewindow,
34 34 HistoryWindowType,
35 35 initModelFromDefaultTimewindow,
  36 + QuickTimeIntervalTranslationMap,
  37 + RealtimeWindowType,
36 38 Timewindow,
37 39 TimewindowType
38 40 } from '@shared/models/time/time.models';
... ... @@ -272,14 +274,20 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
272 274
273 275 updateDisplayValue() {
274 276 if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) {
275   - this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - ' +
276   - this.translate.instant('timewindow.last-prefix') + ' ' +
277   - this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs);
  277 + this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - ';
  278 + if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) {
  279 + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.realtime.quickInterval));
  280 + } else {
  281 + this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' +
  282 + this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs);
  283 + }
278 284 } else {
279 285 this.innerValue.displayValue = !this.historyOnly ? (this.translate.instant('timewindow.history') + ' - ') : '';
280 286 if (this.innerValue.history.historyType === HistoryWindowType.LAST_INTERVAL) {
281 287 this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' +
282 288 this.millisecondsToTimeStringPipe.transform(this.innerValue.history.timewindowMs);
  289 + } else if (this.innerValue.history.historyType === HistoryWindowType.INTERVAL) {
  290 + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.history.quickInterval));
283 291 } else {
284 292 const startString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.startTimeMs, 'yyyy-MM-dd HH:mm:ss');
285 293 const endString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.endTimeMs, 'yyyy-MM-dd HH:mm:ss');
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17 import { TimeService } from '@core/services/time.service';
18   -import { deepClone, isDefined, isUndefined } from '@app/core/utils';
  18 +import { deepClone, isDefined, isDefinedAndNotNull, isUndefined } from '@app/core/utils';
19 19 import * as moment_ from 'moment';
20 20 import { Observable } from 'rxjs/internal/Observable';
21 21 import { from, of } from 'rxjs';
... ... @@ -27,6 +27,7 @@ export const SECOND = 1000;
27 27 export const MINUTE = 60 * SECOND;
28 28 export const HOUR = 60 * MINUTE;
29 29 export const DAY = 24 * HOUR;
  30 +export const WEEK = 7 * DAY;
30 31 export const YEAR = DAY * 365;
31 32
32 33 export enum TimewindowType {
... ... @@ -34,14 +35,25 @@ export enum TimewindowType {
34 35 HISTORY
35 36 }
36 37
  38 +export enum RealtimeWindowType {
  39 + LAST_INTERVAL,
  40 + INTERVAL
  41 +}
  42 +
37 43 export enum HistoryWindowType {
38 44 LAST_INTERVAL,
39   - FIXED
  45 + FIXED,
  46 + INTERVAL
40 47 }
41 48
42 49 export interface IntervalWindow {
43 50 interval?: number;
44 51 timewindowMs?: number;
  52 + quickInterval?: QuickTimeInterval;
  53 +}
  54 +
  55 +export interface RealtimeWindow extends IntervalWindow{
  56 + realtimeType?: RealtimeWindowType;
45 57 }
46 58
47 59 export interface FixedWindow {
... ... @@ -86,7 +98,7 @@ export interface Timewindow {
86 98 hideAggregation?: boolean;
87 99 hideAggInterval?: boolean;
88 100 selectedTab?: TimewindowType;
89   - realtime?: IntervalWindow;
  101 + realtime?: RealtimeWindow;
90 102 history?: HistoryWindow;
91 103 aggregation?: Aggregation;
92 104 }
... ... @@ -99,6 +111,7 @@ export interface SubscriptionAggregation extends Aggregation {
99 111
100 112 export interface SubscriptionTimewindow {
101 113 startTs?: number;
  114 + quickInterval?: QuickTimeInterval;
102 115 realtimeWindowMs?: number;
103 116 fixedWindow?: FixedWindow;
104 117 aggregation?: SubscriptionAggregation;
... ... @@ -111,6 +124,42 @@ export interface WidgetTimewindow {
111 124 stDiff?: number;
112 125 }
113 126
  127 +export enum QuickTimeInterval {
  128 + YESTERDAY = 'YESTERDAY',
  129 + DAY_BEFORE_YESTERDAY = 'DAY_BEFORE_YESTERDAY',
  130 + THIS_DAY_LAST_WEEK = 'THIS_DAY_LAST_WEEK',
  131 + PREVIOUS_WEEK = 'PREVIOUS_WEEK',
  132 + PREVIOUS_MONTH = 'PREVIOUS_MONTH',
  133 + PREVIOUS_YEAR = 'PREVIOUS_YEAR',
  134 + CURRENT_HOUR = 'CURRENT_HOUR',
  135 + CURRENT_DAY = 'CURRENT_DAY',
  136 + CURRENT_DAY_SO_FAR = 'CURRENT_DAY_SO_FAR',
  137 + CURRENT_WEEK = 'CURRENT_WEEK',
  138 + CURRENT_WEEK_SO_FAR = 'CURRENT_WEEK_SO_WAR',
  139 + CURRENT_MONTH = 'CURRENT_MONTH',
  140 + CURRENT_MONTH_SO_FAR = 'CURRENT_MONTH_SO_FAR',
  141 + CURRENT_YEAR = 'CURRENT_YEAR',
  142 + CURRENT_YEAR_SO_FAR = 'CURRENT_YEAR_SO_FAR'
  143 +}
  144 +
  145 +export const QuickTimeIntervalTranslationMap = new Map<QuickTimeInterval, string>([
  146 + [QuickTimeInterval.YESTERDAY, 'timeinterval.predefined.yesterday'],
  147 + [QuickTimeInterval.DAY_BEFORE_YESTERDAY, 'timeinterval.predefined.day-before-yesterday'],
  148 + [QuickTimeInterval.THIS_DAY_LAST_WEEK, 'timeinterval.predefined.this-day-last-week'],
  149 + [QuickTimeInterval.PREVIOUS_WEEK, 'timeinterval.predefined.previous-week'],
  150 + [QuickTimeInterval.PREVIOUS_MONTH, 'timeinterval.predefined.previous-month'],
  151 + [QuickTimeInterval.PREVIOUS_YEAR, 'timeinterval.predefined.previous-year'],
  152 + [QuickTimeInterval.CURRENT_HOUR, 'timeinterval.predefined.current-hour'],
  153 + [QuickTimeInterval.CURRENT_DAY, 'timeinterval.predefined.current-day'],
  154 + [QuickTimeInterval.CURRENT_DAY_SO_FAR, 'timeinterval.predefined.current-day-so-far'],
  155 + [QuickTimeInterval.CURRENT_WEEK, 'timeinterval.predefined.current-week'],
  156 + [QuickTimeInterval.CURRENT_WEEK_SO_FAR, 'timeinterval.predefined.current-week-so-far'],
  157 + [QuickTimeInterval.CURRENT_MONTH, 'timeinterval.predefined.current-month'],
  158 + [QuickTimeInterval.CURRENT_MONTH_SO_FAR, 'timeinterval.predefined.current-month-so-far'],
  159 + [QuickTimeInterval.CURRENT_YEAR, 'timeinterval.predefined.current-year'],
  160 + [QuickTimeInterval.CURRENT_YEAR_SO_FAR, 'timeinterval.predefined.current-year-so-far']
  161 +]);
  162 +
114 163 export function historyInterval(timewindowMs: number): Timewindow {
115 164 const timewindow: Timewindow = {
116 165 selectedTab: TimewindowType.HISTORY,
... ... @@ -131,8 +180,10 @@ export function defaultTimewindow(timeService: TimeService): Timewindow {
131 180 hideAggInterval: false,
132 181 selectedTab: TimewindowType.REALTIME,
133 182 realtime: {
  183 + realtimeType: RealtimeWindowType.LAST_INTERVAL,
134 184 interval: SECOND,
135   - timewindowMs: MINUTE
  185 + timewindowMs: MINUTE,
  186 + quickInterval: QuickTimeInterval.CURRENT_DAY
136 187 },
137 188 history: {
138 189 historyType: HistoryWindowType.LAST_INTERVAL,
... ... @@ -141,7 +192,8 @@ export function defaultTimewindow(timeService: TimeService): Timewindow {
141 192 fixedTimewindow: {
142 193 startTimeMs: currentTime - DAY,
143 194 endTimeMs: currentTime
144   - }
  195 + },
  196 + quickInterval: QuickTimeInterval.CURRENT_DAY
145 197 },
146 198 aggregation: {
147 199 type: AggregationType.AVG,
... ... @@ -170,7 +222,20 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T
170 222 if (isDefined(value.realtime.interval)) {
171 223 model.realtime.interval = value.realtime.interval;
172 224 }
173   - model.realtime.timewindowMs = value.realtime.timewindowMs;
  225 + if (isUndefined(value.realtime.realtimeType)) {
  226 + if (isDefined(value.realtime.quickInterval)) {
  227 + model.realtime.realtimeType = RealtimeWindowType.INTERVAL;
  228 + } else {
  229 + model.realtime.realtimeType = RealtimeWindowType.LAST_INTERVAL;
  230 + }
  231 + } else {
  232 + model.realtime.realtimeType = value.realtime.realtimeType;
  233 + }
  234 + if (model.realtime.realtimeType === RealtimeWindowType.INTERVAL) {
  235 + model.realtime.quickInterval = value.realtime.quickInterval;
  236 + } else {
  237 + model.realtime.timewindowMs = value.realtime.timewindowMs;
  238 + }
174 239 } else {
175 240 if (isDefined(value.history.interval)) {
176 241 model.history.interval = value.history.interval;
... ... @@ -178,6 +243,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T
178 243 if (isUndefined(value.history.historyType)) {
179 244 if (isDefined(value.history.timewindowMs)) {
180 245 model.history.historyType = HistoryWindowType.LAST_INTERVAL;
  246 + } else if (isDefined(value.history.quickInterval)) {
  247 + model.history.historyType = HistoryWindowType.INTERVAL;
181 248 } else {
182 249 model.history.historyType = HistoryWindowType.FIXED;
183 250 }
... ... @@ -186,6 +253,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T
186 253 }
187 254 if (model.history.historyType === HistoryWindowType.LAST_INTERVAL) {
188 255 model.history.timewindowMs = value.history.timewindowMs;
  256 + } else if (model.history.historyType === HistoryWindowType.INTERVAL) {
  257 + model.history.quickInterval = value.history.quickInterval;
189 258 } else {
190 259 model.history.fixedTimewindow.startTimeMs = value.history.fixedTimewindow.startTimeMs;
191 260 model.history.fixedTimewindow.endTimeMs = value.history.fixedTimewindow.endTimeMs;
... ... @@ -267,21 +336,40 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num
267 336 selectedTab = isDefined(timewindow.realtime) ? TimewindowType.REALTIME : TimewindowType.HISTORY;
268 337 }
269 338 if (selectedTab === TimewindowType.REALTIME) {
270   - subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs;
  339 + let realtimeType = timewindow.realtime.realtimeType;
  340 + if (isUndefined(realtimeType)) {
  341 + if (isDefined(timewindow.realtime.quickInterval)) {
  342 + realtimeType = RealtimeWindowType.INTERVAL;
  343 + } else {
  344 + realtimeType = RealtimeWindowType.LAST_INTERVAL;
  345 + }
  346 + }
  347 + if (realtimeType === RealtimeWindowType.INTERVAL) {
  348 + subscriptionTimewindow.realtimeWindowMs = getSubscriptionRealtimeWindowFromTimeInterval(timewindow.realtime.quickInterval);
  349 + subscriptionTimewindow.quickInterval = timewindow.realtime.quickInterval;
  350 + } else {
  351 + subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs;
  352 + }
271 353 subscriptionTimewindow.aggregation.interval =
272 354 timeService.boundIntervalToTimewindow(subscriptionTimewindow.realtimeWindowMs, timewindow.realtime.interval,
273 355 subscriptionTimewindow.aggregation.type);
274 356 subscriptionTimewindow.startTs = Date.now() + stDiff - subscriptionTimewindow.realtimeWindowMs;
275 357 const startDiff = subscriptionTimewindow.startTs % subscriptionTimewindow.aggregation.interval;
276 358 aggTimewindow = subscriptionTimewindow.realtimeWindowMs;
277   - if (startDiff) {
  359 + if (startDiff && realtimeType !== RealtimeWindowType.INTERVAL) {
278 360 subscriptionTimewindow.startTs -= startDiff;
279 361 aggTimewindow += subscriptionTimewindow.aggregation.interval;
280 362 }
281 363 } else {
282 364 let historyType = timewindow.history.historyType;
283 365 if (isUndefined(historyType)) {
284   - historyType = isDefined(timewindow.history.timewindowMs) ? HistoryWindowType.LAST_INTERVAL : HistoryWindowType.FIXED;
  366 + if (isDefined(timewindow.history.timewindowMs)) {
  367 + historyType = HistoryWindowType.LAST_INTERVAL;
  368 + } else if (isDefined(timewindow.history.quickInterval)) {
  369 + historyType = HistoryWindowType.INTERVAL;
  370 + } else {
  371 + historyType = HistoryWindowType.FIXED;
  372 + }
285 373 }
286 374 if (historyType === HistoryWindowType.LAST_INTERVAL) {
287 375 const currentTime = Date.now();
... ... @@ -290,6 +378,13 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num
290 378 endTimeMs: currentTime
291 379 };
292 380 aggTimewindow = timewindow.history.timewindowMs;
  381 + } else if (historyType === HistoryWindowType.INTERVAL) {
  382 + const currentDate = moment();
  383 + subscriptionTimewindow.fixedWindow = {
  384 + startTimeMs: calculateIntervalStartTime(timewindow.history.quickInterval, null, currentDate),
  385 + endTimeMs: calculateIntervalEndTime(timewindow.history.quickInterval, null, currentDate)
  386 + };
  387 + aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs;
293 388 } else {
294 389 subscriptionTimewindow.fixedWindow = {
295 390 startTimeMs: timewindow.history.fixedTimewindow.startTimeMs,
... ... @@ -309,6 +404,132 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num
309 404 return subscriptionTimewindow;
310 405 }
311 406
  407 +function getSubscriptionRealtimeWindowFromTimeInterval(interval: QuickTimeInterval): number {
  408 + const currentDate = moment();
  409 + switch (interval) {
  410 + case QuickTimeInterval.CURRENT_HOUR:
  411 + return currentDate.diff(currentDate.clone().startOf('hour'))
  412 + case QuickTimeInterval.CURRENT_DAY:
  413 + case QuickTimeInterval.CURRENT_DAY_SO_FAR:
  414 + return currentDate.diff(currentDate.clone().startOf('day'));
  415 + case QuickTimeInterval.CURRENT_WEEK:
  416 + case QuickTimeInterval.CURRENT_WEEK_SO_FAR:
  417 + return currentDate.diff(currentDate.clone().startOf('week'));
  418 + case QuickTimeInterval.CURRENT_MONTH:
  419 + case QuickTimeInterval.CURRENT_MONTH_SO_FAR:
  420 + return currentDate.diff(currentDate.clone().startOf('month'));
  421 + case QuickTimeInterval.CURRENT_YEAR:
  422 + case QuickTimeInterval.CURRENT_YEAR_SO_FAR:
  423 + return currentDate.diff(currentDate.clone().startOf('year'));
  424 + }
  425 +}
  426 +
  427 +export function calculateIntervalEndTime(interval: QuickTimeInterval, endTs = 0, nowDate?: moment_.Moment): number {
  428 + const currentDate = isDefinedAndNotNull(nowDate) ? nowDate.clone() : moment();
  429 + switch (interval) {
  430 + case QuickTimeInterval.YESTERDAY:
  431 + currentDate.subtract(1, 'days');
  432 + return currentDate.endOf('day').valueOf();
  433 + case QuickTimeInterval.DAY_BEFORE_YESTERDAY:
  434 + currentDate.subtract(2, 'days');
  435 + return currentDate.endOf('day').valueOf();
  436 + case QuickTimeInterval.THIS_DAY_LAST_WEEK:
  437 + currentDate.subtract(1, 'weeks');
  438 + return currentDate.endOf('day').valueOf();
  439 + case QuickTimeInterval.PREVIOUS_WEEK:
  440 + currentDate.subtract(1, 'weeks');
  441 + return currentDate.endOf('week').valueOf();
  442 + case QuickTimeInterval.PREVIOUS_MONTH:
  443 + currentDate.subtract(1, 'months');
  444 + return currentDate.endOf('month').valueOf();
  445 + case QuickTimeInterval.PREVIOUS_YEAR:
  446 + currentDate.subtract(1, 'years');
  447 + return currentDate.endOf('year').valueOf();
  448 + case QuickTimeInterval.CURRENT_HOUR:
  449 + return currentDate.endOf('hour').valueOf();
  450 + case QuickTimeInterval.CURRENT_DAY:
  451 + return currentDate.endOf('day').valueOf();
  452 + case QuickTimeInterval.CURRENT_WEEK:
  453 + return currentDate.endOf('week').valueOf();
  454 + case QuickTimeInterval.CURRENT_MONTH:
  455 + return currentDate.endOf('month').valueOf();
  456 + case QuickTimeInterval.CURRENT_YEAR:
  457 + return currentDate.endOf('year').valueOf();
  458 + case QuickTimeInterval.CURRENT_DAY_SO_FAR:
  459 + case QuickTimeInterval.CURRENT_WEEK_SO_FAR:
  460 + case QuickTimeInterval.CURRENT_MONTH_SO_FAR:
  461 + case QuickTimeInterval.CURRENT_YEAR_SO_FAR:
  462 + return currentDate.valueOf();
  463 + default:
  464 + return endTs;
  465 + }
  466 +}
  467 +
  468 +export function calculateIntervalStartTime(interval: QuickTimeInterval, startTS = 0, nowDate?: moment_.Moment): number {
  469 + const currentDate = isDefinedAndNotNull(nowDate) ? nowDate.clone() : moment();
  470 + switch (interval) {
  471 + case QuickTimeInterval.YESTERDAY:
  472 + currentDate.subtract(1, 'days');
  473 + return currentDate.startOf('day').valueOf();
  474 + case QuickTimeInterval.DAY_BEFORE_YESTERDAY:
  475 + currentDate.subtract(2, 'days');
  476 + return currentDate.startOf('day').valueOf();
  477 + case QuickTimeInterval.THIS_DAY_LAST_WEEK:
  478 + currentDate.subtract(1, 'weeks');
  479 + return currentDate.startOf('day').valueOf();
  480 + case QuickTimeInterval.PREVIOUS_WEEK:
  481 + currentDate.subtract(1, 'weeks');
  482 + return currentDate.startOf('week').valueOf();
  483 + case QuickTimeInterval.PREVIOUS_MONTH:
  484 + currentDate.subtract(1, 'months');
  485 + return currentDate.startOf('month').valueOf();
  486 + case QuickTimeInterval.PREVIOUS_YEAR:
  487 + currentDate.subtract(1, 'years');
  488 + return currentDate.startOf('year').valueOf();
  489 + case QuickTimeInterval.CURRENT_HOUR:
  490 + return currentDate.startOf('hour').valueOf();
  491 + case QuickTimeInterval.CURRENT_DAY:
  492 + case QuickTimeInterval.CURRENT_DAY_SO_FAR:
  493 + return currentDate.startOf('day').valueOf();
  494 + case QuickTimeInterval.CURRENT_WEEK:
  495 + case QuickTimeInterval.CURRENT_WEEK_SO_FAR:
  496 + return currentDate.startOf('week').valueOf();
  497 + case QuickTimeInterval.CURRENT_MONTH:
  498 + case QuickTimeInterval.CURRENT_MONTH_SO_FAR:
  499 + return currentDate.startOf('month').valueOf();
  500 + case QuickTimeInterval.CURRENT_YEAR:
  501 + case QuickTimeInterval.CURRENT_YEAR_SO_FAR:
  502 + return currentDate.startOf('year').valueOf();
  503 + default:
  504 + return startTS;
  505 + }
  506 +}
  507 +
  508 +export function quickTimeIntervalPeriod(interval: QuickTimeInterval): number {
  509 + switch (interval) {
  510 + case QuickTimeInterval.CURRENT_HOUR:
  511 + return HOUR;
  512 + case QuickTimeInterval.YESTERDAY:
  513 + case QuickTimeInterval.DAY_BEFORE_YESTERDAY:
  514 + case QuickTimeInterval.THIS_DAY_LAST_WEEK:
  515 + case QuickTimeInterval.CURRENT_DAY:
  516 + case QuickTimeInterval.CURRENT_DAY_SO_FAR:
  517 + return DAY;
  518 + case QuickTimeInterval.PREVIOUS_WEEK:
  519 + case QuickTimeInterval.CURRENT_WEEK:
  520 + case QuickTimeInterval.CURRENT_WEEK_SO_FAR:
  521 + return WEEK;
  522 + case QuickTimeInterval.PREVIOUS_MONTH:
  523 + case QuickTimeInterval.CURRENT_MONTH:
  524 + case QuickTimeInterval.CURRENT_MONTH_SO_FAR:
  525 + return DAY * 30;
  526 + case QuickTimeInterval.PREVIOUS_YEAR:
  527 + case QuickTimeInterval.CURRENT_YEAR:
  528 + case QuickTimeInterval.CURRENT_YEAR_SO_FAR:
  529 + return YEAR;
  530 + }
  531 +}
  532 +
312 533 export function createTimewindowForComparison(subscriptionTimewindow: SubscriptionTimewindow,
313 534 timeUnit: moment_.unitOfTime.DurationConstructor): SubscriptionTimewindow {
314 535 const timewindowForComparison: SubscriptionTimewindow = {
... ... @@ -358,6 +579,8 @@ export function cloneSelectedHistoryTimewindow(historyWindow: HistoryWindow): Hi
358 579 cloned.interval = historyWindow.interval;
359 580 if (historyWindow.historyType === HistoryWindowType.LAST_INTERVAL) {
360 581 cloned.timewindowMs = historyWindow.timewindowMs;
  582 + } else if (historyWindow.historyType === HistoryWindowType.INTERVAL) {
  583 + cloned.quickInterval = historyWindow.quickInterval;
361 584 } else if (historyWindow.historyType === HistoryWindowType.FIXED) {
362 585 cloned.fixedTimewindow = deepClone(historyWindow.fixedTimewindow);
363 586 }
... ... @@ -375,7 +598,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>(
375 598 {
376 599 name: 'timeinterval.seconds-interval',
377 600 translateParams: {seconds: 1},
378   - value: 1 * SECOND
  601 + value: SECOND
379 602 },
380 603 {
381 604 name: 'timeinterval.seconds-interval',
... ... @@ -400,7 +623,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>(
400 623 {
401 624 name: 'timeinterval.minutes-interval',
402 625 translateParams: {minutes: 1},
403   - value: 1 * MINUTE
  626 + value: MINUTE
404 627 },
405 628 {
406 629 name: 'timeinterval.minutes-interval',
... ... @@ -430,7 +653,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>(
430 653 {
431 654 name: 'timeinterval.hours-interval',
432 655 translateParams: {hours: 1},
433   - value: 1 * HOUR
  656 + value: HOUR
434 657 },
435 658 {
436 659 name: 'timeinterval.hours-interval',
... ... @@ -455,7 +678,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>(
455 678 {
456 679 name: 'timeinterval.days-interval',
457 680 translateParams: {days: 1},
458   - value: 1 * DAY
  681 + value: DAY
459 682 },
460 683 {
461 684 name: 'timeinterval.days-interval',
... ...
... ... @@ -140,6 +140,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select
140 140 import { FileSizePipe } from '@shared/pipe/file-size.pipe';
141 141 import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component';
142 142 import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
  143 +import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component';
143 144
144 145 @NgModule({
145 146 providers: [
... ... @@ -175,6 +176,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
175 176 TimewindowComponent,
176 177 TimewindowPanelComponent,
177 178 TimeintervalComponent,
  179 + QuickTimeIntervalComponent,
178 180 DashboardSelectComponent,
179 181 DashboardSelectPanelComponent,
180 182 DatetimePeriodComponent,
... ... @@ -302,6 +304,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
302 304 TimewindowComponent,
303 305 TimewindowPanelComponent,
304 306 TimeintervalComponent,
  307 + QuickTimeIntervalComponent,
305 308 DashboardSelectComponent,
306 309 DatetimePeriodComponent,
307 310 DatetimeComponent,
... ...
... ... @@ -2118,7 +2118,24 @@
2118 2118 "hours": "Hours",
2119 2119 "minutes": "Minutes",
2120 2120 "seconds": "Seconds",
2121   - "advanced": "Advanced"
  2121 + "advanced": "Advanced",
  2122 + "predefined": {
  2123 + "yesterday": "Yesterday",
  2124 + "day-before-yesterday": "Day before yesterday",
  2125 + "this-day-last-week": "This day last week",
  2126 + "previous-week": "Previous week",
  2127 + "previous-month": "Previous month",
  2128 + "previous-year": "Previous year",
  2129 + "current-hour": "Current hour",
  2130 + "current-day": "Current day",
  2131 + "current-day-so-far": "Current day so far",
  2132 + "current-week": "Current week",
  2133 + "current-week-so-far": "Current week so far",
  2134 + "current-month": "Current month",
  2135 + "current-month-so-far": "Current month so far",
  2136 + "current-year": "Current year",
  2137 + "current-year-so-far": "Current year so far"
  2138 + }
2122 2139 },
2123 2140 "timeunit": {
2124 2141 "seconds": "Seconds",
... ... @@ -2139,7 +2156,8 @@
2139 2156 "date-range": "Date range",
2140 2157 "last": "Last",
2141 2158 "time-period": "Time period",
2142   - "hide": "Hide"
  2159 + "hide": "Hide",
  2160 + "interval": "Interval"
2143 2161 },
2144 2162 "user": {
2145 2163 "user": "User",
... ...