From f1193c1d5b082d13f62a25bda15dc3c335fff89b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 15 Mar 2021 16:29:42 +0200 Subject: [PATCH 1/4] UI: Added new history time window type - interval --- .../time/quick-time-interval.component.html | 27 +++ .../time/quick-time-interval.component.scss | 19 ++ .../time/quick-time-interval.component.ts | 80 +++++++++ .../time/timewindow-panel.component.html | 11 ++ .../time/timewindow-panel.component.ts | 16 +- .../components/time/timewindow.component.ts | 3 + .../src/app/shared/models/time/time.models.ts | 163 +++++++++++++++++- ui-ngx/src/app/shared/shared.module.ts | 3 + .../assets/locale/locale.constant-en_US.json | 21 ++- 9 files changed, 331 insertions(+), 12 deletions(-) create mode 100644 ui-ngx/src/app/shared/components/time/quick-time-interval.component.html create mode 100644 ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss create mode 100644 ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.html b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.html new file mode 100644 index 0000000000..c9f367c574 --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.html @@ -0,0 +1,27 @@ + +
+ + timewindow.interval + + + {{ timeIntervalTranslationMap.get(interval) | translate}} + + + +
diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss new file mode 100644 index 0000000000..97c400525e --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * 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. + */ + +:host { + min-width: 364px; +} diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts new file mode 100644 index 0000000000..7f0ccb90db --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// 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 { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { QuickTimeInterval, QuickTimeIntervalTranslationMap } from '@shared/models/time/time.models'; + +@Component({ + selector: 'tb-quick-time-interval', + templateUrl: './quick-time-interval.component.html', + styleUrls: ['./quick-time-interval.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => QuickTimeIntervalComponent), + multi: true + } + ] +}) +export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor { + + private allIntervals = Object.values(QuickTimeInterval); + + modelValue: QuickTimeInterval; + timeIntervalTranslationMap = QuickTimeIntervalTranslationMap; + + rendered = false; + + @Input() disabled: boolean; + + @Input() onlyCurrentInterval = false; + + private propagateChange = (_: any) => {}; + + constructor() { + } + + get intervals() { + if (this.onlyCurrentInterval) { + return this.allIntervals.filter(interval => interval.startsWith('TODAY_') || interval.startsWith('CURRENT_')); + } + return this.allIntervals; + } + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(interval: QuickTimeInterval): void { + this.modelValue = interval; + this.rendered = true; + } + + onIntervalChange() { + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html index e4ddaa0f56..432b84749a 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html @@ -65,6 +65,17 @@ style="padding-top: 8px;"> + +
+ timewindow.interval + +
+
diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index aeb9c265e1..b4aefef859 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -19,7 +19,7 @@ import { aggregationTranslations, AggregationType, DAY, - HistoryWindowType, + HistoryWindowType, quickTimeIntervalPeriod, Timewindow, TimewindowType } from '@shared/models/time/time.models'; @@ -119,7 +119,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { value: this.timewindow.history && typeof this.timewindow.history.fixedTimewindow !== 'undefined' ? this.timewindow.history.fixedTimewindow : null, disabled: hideInterval - }) + }), + quickInterval: this.fb.control({ + value: this.timewindow.history && typeof this.timewindow.history.quickInterval !== 'undefined' + ? this.timewindow.history.quickInterval : null, + disabled: hideInterval + }), } ), aggregation: this.fb.group( @@ -149,7 +154,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { historyType: timewindowFormValue.history.historyType, timewindowMs: timewindowFormValue.history.timewindowMs, interval: timewindowFormValue.history.interval, - fixedTimewindow: timewindowFormValue.history.fixedTimewindow + fixedTimewindow: timewindowFormValue.history.fixedTimewindow, + quickInterval: timewindowFormValue.history.quickInterval }; if (this.aggregation) { this.timewindow.aggregation = { @@ -193,6 +199,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { const timewindowFormValue = this.timewindowForm.getRawValue(); if (timewindowFormValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { return timewindowFormValue.history.timewindowMs; + } else if (timewindowFormValue.history.historyType === HistoryWindowType.INTERVAL) { + return quickTimeIntervalPeriod(timewindowFormValue.history.quickInterval); } else if (timewindowFormValue.history.fixedTimewindow) { return timewindowFormValue.history.fixedTimewindow.endTimeMs - timewindowFormValue.history.fixedTimewindow.startTimeMs; @@ -206,10 +214,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { this.timewindowForm.get('history.historyType').disable({emitEvent: false}); this.timewindowForm.get('history.timewindowMs').disable({emitEvent: false}); this.timewindowForm.get('history.fixedTimewindow').disable({emitEvent: false}); + this.timewindowForm.get('history.quickInterval').disable({emitEvent: false}); } else { this.timewindowForm.get('history.historyType').enable({emitEvent: false}); this.timewindowForm.get('history.timewindowMs').enable({emitEvent: false}); this.timewindowForm.get('history.fixedTimewindow').enable({emitEvent: false}); + this.timewindowForm.get('history.quickInterval').enable({emitEvent: false}); } this.timewindowForm.markAsDirty(); } diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index e1be42eff3..59eada88a9 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -33,6 +33,7 @@ import { cloneSelectedTimewindow, HistoryWindowType, initModelFromDefaultTimewindow, + QuickTimeIntervalTranslationMap, Timewindow, TimewindowType } from '@shared/models/time/time.models'; @@ -280,6 +281,8 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces if (this.innerValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' + this.millisecondsToTimeStringPipe.transform(this.innerValue.history.timewindowMs); + } else if (this.innerValue.history.historyType === HistoryWindowType.INTERVAL) { + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.history.quickInterval)); } else { const startString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.startTimeMs, 'yyyy-MM-dd HH:mm:ss'); const endString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.endTimeMs, 'yyyy-MM-dd HH:mm:ss'); diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index 1156840915..ee3748e214 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -27,6 +27,7 @@ export const SECOND = 1000; export const MINUTE = 60 * SECOND; export const HOUR = 60 * MINUTE; export const DAY = 24 * HOUR; +export const WEEK = 7 * DAY; export const YEAR = DAY * 365; export enum TimewindowType { @@ -36,12 +37,14 @@ export enum TimewindowType { export enum HistoryWindowType { LAST_INTERVAL, - FIXED + FIXED, + INTERVAL } export interface IntervalWindow { interval?: number; timewindowMs?: number; + quickInterval?: QuickTimeInterval; } export interface FixedWindow { @@ -111,6 +114,40 @@ export interface WidgetTimewindow { stDiff?: number; } +export enum QuickTimeInterval { + YESTERDAY = 'YESTERDAY', + DAY_BEFORE_YESTERDAY = 'DAY_BEFORE_YESTERDAY', + THIS_DAY_LAST_WEEK = 'THIS_DAY_LAST_WEEK', + PREVIOUS_WEEK = 'PREVIOUS_WEEK', + PREVIOUS_MONTH = 'PREVIOUS_MONTH', + PREVIOUS_YEAR = 'PREVIOUS_YEAR', + TODAY = 'TODAY', + TODAY_SO_FAR = 'TODAY_SO_FAR', + CURRENT_WEEK = 'CURRENT_WEEK', + CURRENT_WEEK_SO_FAR = 'CURRENT_WEEK_SO_WAR', + CURRENT_MONTH = 'CURRENT_MONTH', + CURRENT_MONTH_SO_FAR = 'CURRENT_MONTH_SO_FAR', + CURRENT_YEAR = 'CURRENT_YEAR', + CURRENT_YEAR_SO_FAR = 'CURRENT_YEAR_SO_FAR' +} + +export const QuickTimeIntervalTranslationMap = new Map([ + [QuickTimeInterval.YESTERDAY, 'timeinterval.predefined.yesterday'], + [QuickTimeInterval.DAY_BEFORE_YESTERDAY, 'timeinterval.predefined.day-before-yesterday'], + [QuickTimeInterval.THIS_DAY_LAST_WEEK, 'timeinterval.predefined.this-day-last-week'], + [QuickTimeInterval.PREVIOUS_WEEK, 'timeinterval.predefined.previous-week'], + [QuickTimeInterval.PREVIOUS_MONTH, 'timeinterval.predefined.previous-month'], + [QuickTimeInterval.PREVIOUS_YEAR, 'timeinterval.predefined.previous-year'], + [QuickTimeInterval.TODAY, 'timeinterval.predefined.today'], + [QuickTimeInterval.TODAY_SO_FAR, 'timeinterval.predefined.today-so-far'], + [QuickTimeInterval.CURRENT_WEEK, 'timeinterval.predefined.current-week'], + [QuickTimeInterval.CURRENT_WEEK_SO_FAR, 'timeinterval.predefined.current-week-so-far'], + [QuickTimeInterval.CURRENT_MONTH, 'timeinterval.predefined.current-month'], + [QuickTimeInterval.CURRENT_MONTH_SO_FAR, 'timeinterval.predefined.current-month-so-far'], + [QuickTimeInterval.CURRENT_YEAR, 'timeinterval.predefined.current-year'], + [QuickTimeInterval.CURRENT_YEAR_SO_FAR, 'timeinterval.predefined.current-year-so-far'] +]); + export function historyInterval(timewindowMs: number): Timewindow { const timewindow: Timewindow = { selectedTab: TimewindowType.HISTORY, @@ -141,7 +178,8 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { fixedTimewindow: { startTimeMs: currentTime - DAY, endTimeMs: currentTime - } + }, + quickInterval: QuickTimeInterval.TODAY }, aggregation: { type: AggregationType.AVG, @@ -178,6 +216,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T if (isUndefined(value.history.historyType)) { if (isDefined(value.history.timewindowMs)) { model.history.historyType = HistoryWindowType.LAST_INTERVAL; + } else if (isDefined(value.history.quickInterval)) { + model.history.historyType = HistoryWindowType.INTERVAL; } else { model.history.historyType = HistoryWindowType.FIXED; } @@ -186,6 +226,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T } if (model.history.historyType === HistoryWindowType.LAST_INTERVAL) { model.history.timewindowMs = value.history.timewindowMs; + } else if (model.history.historyType === HistoryWindowType.INTERVAL) { + model.history.quickInterval = value.history.quickInterval; } else { model.history.fixedTimewindow.startTimeMs = value.history.fixedTimewindow.startTimeMs; model.history.fixedTimewindow.endTimeMs = value.history.fixedTimewindow.endTimeMs; @@ -281,7 +323,13 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num } else { let historyType = timewindow.history.historyType; if (isUndefined(historyType)) { - historyType = isDefined(timewindow.history.timewindowMs) ? HistoryWindowType.LAST_INTERVAL : HistoryWindowType.FIXED; + if (isDefined(timewindow.history.timewindowMs)) { + historyType = HistoryWindowType.LAST_INTERVAL; + } else if (isDefined(timewindow.history.quickInterval)) { + historyType = HistoryWindowType.INTERVAL; + } else { + historyType = HistoryWindowType.FIXED; + } } if (historyType === HistoryWindowType.LAST_INTERVAL) { const currentTime = Date.now(); @@ -290,6 +338,9 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num endTimeMs: currentTime }; aggTimewindow = timewindow.history.timewindowMs; + } else if (historyType === HistoryWindowType.INTERVAL) { + subscriptionTimewindow.fixedWindow = createSubscriptionTimeWindowFromQuickKTimeInterval(timewindow.history.quickInterval); + aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; } else { subscriptionTimewindow.fixedWindow = { startTimeMs: timewindow.history.fixedTimewindow.startTimeMs, @@ -309,6 +360,102 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num return subscriptionTimewindow; } +export function createSubscriptionTimeWindowFromQuickKTimeInterval(interval: QuickTimeInterval): FixedWindow { + const currentDate = moment(); + const timeWindow = { + startTimeMs: 0, + endTimeMs: 0 + }; + switch (interval) { + case QuickTimeInterval.YESTERDAY: + currentDate.subtract(1, 'days'); + timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); + break; + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: + currentDate.subtract(2, 'days'); + timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); + break; + case QuickTimeInterval.THIS_DAY_LAST_WEEK: + currentDate.subtract(1, 'weeks'); + timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); + break; + case QuickTimeInterval.PREVIOUS_WEEK: + currentDate.subtract(1, 'weeks'); + timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('week').valueOf(); + break; + case QuickTimeInterval.PREVIOUS_MONTH: + currentDate.subtract(1, 'months'); + timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('month').valueOf(); + break; + case QuickTimeInterval.PREVIOUS_YEAR: + currentDate.subtract(1, 'years'); + timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('year').valueOf(); + break; + case QuickTimeInterval.TODAY: + timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); + break; + case QuickTimeInterval.TODAY_SO_FAR: + timeWindow.endTimeMs = currentDate.valueOf(); + timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); + break; + case QuickTimeInterval.CURRENT_WEEK: + timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('week').valueOf(); + break; + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: + timeWindow.endTimeMs = currentDate.valueOf(); + timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); + break; + case QuickTimeInterval.CURRENT_MONTH: + timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('month').valueOf(); + break; + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: + timeWindow.endTimeMs = currentDate.valueOf(); + timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); + break; + case QuickTimeInterval.CURRENT_YEAR: + timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); + timeWindow.endTimeMs = currentDate.endOf('year').valueOf(); + break; + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: + timeWindow.endTimeMs = currentDate.valueOf(); + timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); + break; + } + return timeWindow; +} + +export function quickTimeIntervalPeriod(interval: QuickTimeInterval): number { + switch (interval) { + case QuickTimeInterval.YESTERDAY: + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: + case QuickTimeInterval.THIS_DAY_LAST_WEEK: + case QuickTimeInterval.TODAY: + case QuickTimeInterval.TODAY_SO_FAR: + return DAY; + case QuickTimeInterval.PREVIOUS_WEEK: + case QuickTimeInterval.CURRENT_WEEK: + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: + return WEEK; + case QuickTimeInterval.PREVIOUS_MONTH: + case QuickTimeInterval.CURRENT_MONTH: + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: + return DAY * 30; + case QuickTimeInterval.PREVIOUS_YEAR: + case QuickTimeInterval.CURRENT_YEAR: + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: + return YEAR; + } +} + export function createTimewindowForComparison(subscriptionTimewindow: SubscriptionTimewindow, timeUnit: moment_.unitOfTime.DurationConstructor): SubscriptionTimewindow { const timewindowForComparison: SubscriptionTimewindow = { @@ -358,6 +505,8 @@ export function cloneSelectedHistoryTimewindow(historyWindow: HistoryWindow): Hi cloned.interval = historyWindow.interval; if (historyWindow.historyType === HistoryWindowType.LAST_INTERVAL) { cloned.timewindowMs = historyWindow.timewindowMs; + } else if (historyWindow.historyType === HistoryWindowType.INTERVAL) { + cloned.quickInterval = historyWindow.quickInterval; } else if (historyWindow.historyType === HistoryWindowType.FIXED) { cloned.fixedTimewindow = deepClone(historyWindow.fixedTimewindow); } @@ -375,7 +524,7 @@ export const defaultTimeIntervals = new Array( { name: 'timeinterval.seconds-interval', translateParams: {seconds: 1}, - value: 1 * SECOND + value: SECOND }, { name: 'timeinterval.seconds-interval', @@ -400,7 +549,7 @@ export const defaultTimeIntervals = new Array( { name: 'timeinterval.minutes-interval', translateParams: {minutes: 1}, - value: 1 * MINUTE + value: MINUTE }, { name: 'timeinterval.minutes-interval', @@ -430,7 +579,7 @@ export const defaultTimeIntervals = new Array( { name: 'timeinterval.hours-interval', translateParams: {hours: 1}, - value: 1 * HOUR + value: HOUR }, { name: 'timeinterval.hours-interval', @@ -455,7 +604,7 @@ export const defaultTimeIntervals = new Array( { name: 'timeinterval.days-interval', translateParams: {days: 1}, - value: 1 * DAY + value: DAY }, { name: 'timeinterval.days-interval', diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 6b9353b8ae..02cd3b93d4 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -140,6 +140,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select import { FileSizePipe } from '@shared/pipe/file-size.pipe'; import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component'; import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; +import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; @NgModule({ providers: [ @@ -175,6 +176,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; TimewindowComponent, TimewindowPanelComponent, TimeintervalComponent, + QuickTimeIntervalComponent, DashboardSelectComponent, DashboardSelectPanelComponent, DatetimePeriodComponent, @@ -302,6 +304,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; TimewindowComponent, TimewindowPanelComponent, TimeintervalComponent, + QuickTimeIntervalComponent, DashboardSelectComponent, DatetimePeriodComponent, DatetimeComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ec8c1458ef..7508dac060 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2111,7 +2111,23 @@ "hours": "Hours", "minutes": "Minutes", "seconds": "Seconds", - "advanced": "Advanced" + "advanced": "Advanced", + "predefined": { + "yesterday": "Yesterday", + "day-before-yesterday": "Day before yesterday", + "this-day-last-week": "This day last week", + "previous-week": "Previous week", + "previous-month": "Previous month", + "previous-year": "Previous year", + "today": "Today", + "today-so-far": "Today so far", + "current-week": "Current week", + "current-week-so-far": "Current week so far", + "current-month": "Current month", + "current-month-so-far": "Current month so far", + "current-year": "Current year", + "current-year-so-far": "Current year so far" + } }, "timeunit": { "seconds": "Seconds", @@ -2132,7 +2148,8 @@ "date-range": "Date range", "last": "Last", "time-period": "Time period", - "hide": "Hide" + "hide": "Hide", + "interval": "Interval" }, "user": { "user": "User", From 0ebbee02db9a981715bc774f2d99b7aa10be4470 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 16 Mar 2021 12:33:45 +0200 Subject: [PATCH 2/4] UI: Added new timewindow type - realtime interval --- ui-ngx/src/app/core/api/data-aggregator.ts | 23 ++- .../app/core/api/entity-data-subscription.ts | 3 +- .../src/app/core/api/widget-subscription.ts | 10 +- .../time/quick-time-interval.component.ts | 3 +- .../time/timewindow-panel.component.html | 47 ++++- .../time/timewindow-panel.component.ts | 44 ++++- .../components/time/timewindow.component.ts | 11 +- .../src/app/shared/models/time/time.models.ts | 174 ++++++++++++------ 8 files changed, 230 insertions(+), 85 deletions(-) diff --git a/ui-ngx/src/app/core/api/data-aggregator.ts b/ui-ngx/src/app/core/api/data-aggregator.ts index a57ad25d93..e0ebb3cbc1 100644 --- a/ui-ngx/src/app/core/api/data-aggregator.ts +++ b/ui-ngx/src/app/core/api/data-aggregator.ts @@ -15,7 +15,12 @@ /// import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; -import { AggregationType } from '@shared/models/time/time.models'; +import { + AggregationType, + calculateIntervalEndTime, + calculateIntervalStartTime, + QuickTimeInterval +} from '@shared/models/time/time.models'; import { UtilsService } from '@core/services/utils.service'; import { deepClone } from '@core/utils'; import Timeout = NodeJS.Timeout; @@ -92,7 +97,8 @@ export class DataAggregator { private interval: number, private stateData: boolean, private utils: UtilsService, - private ignoreDataUpdateOnIntervalTick: boolean) { + private ignoreDataUpdateOnIntervalTick: boolean, + private quickInterval: QuickTimeInterval) { this.tsKeyNames.forEach((key) => { this.dataBuffer[key] = []; }); @@ -138,7 +144,8 @@ export class DataAggregator { this.startTs = startTs; this.timeWindow = timeWindow; this.interval = interval; - this.endTs = this.startTs + this.timeWindow; + const endTs = this.startTs + this.timeWindow; + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); this.elapsed = 0; this.aggregationTimeout = Math.max(this.interval, 1000); this.resetPending = true; @@ -161,7 +168,8 @@ export class DataAggregator { if (!this.dataReceived) { this.elapsed = 0; this.dataReceived = true; - this.endTs = this.startTs + this.timeWindow; + const endTs = this.startTs + this.timeWindow; + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); } if (this.resetPending) { this.resetPending = false; @@ -197,8 +205,11 @@ export class DataAggregator { if (!history) { const delta = Math.floor(this.elapsed / this.interval); if (delta || !this.data) { - this.startTs += delta * this.interval; - this.endTs += delta * this.interval; + const tickTs = delta * this.interval; + const startTS = this.startTs + tickTs; + this.startTs = calculateIntervalStartTime(this.quickInterval, startTS); + const endTs = this.endTs + tickTs; + this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); this.data = this.updateData(); this.elapsed = this.elapsed - delta * this.interval; } diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index dbc9bd2adb..6dc8036df6 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -752,7 +752,8 @@ export class EntityDataSubscription { subsTw.aggregation.interval, subsTw.aggregation.stateData, this.utils, - this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick + this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick, + subsTw.quickInterval ); } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index ce36f57e10..d33329f1cc 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -37,6 +37,8 @@ import { } from '@app/shared/models/widget.models'; import { HttpErrorResponse } from '@angular/common/http'; import { + calculateIntervalEndTime, + calculateIntervalStartTime, createSubscriptionTimewindow, createTimewindowForComparison, SubscriptionTimewindow, @@ -1081,8 +1083,10 @@ export class WidgetSubscription implements IWidgetSubscription { private updateTimewindow() { this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000; if (this.subscriptionTimewindow.realtimeWindowMs) { - this.timeWindow.maxTime = moment().valueOf() + this.timeWindow.stDiff; - this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; + this.timeWindow.maxTime = calculateIntervalEndTime( + this.subscriptionTimewindow.quickInterval, moment().valueOf() + this.timeWindow.stDiff); + this.timeWindow.minTime = calculateIntervalStartTime( + this.subscriptionTimewindow.quickInterval, this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs); } else if (this.subscriptionTimewindow.fixedWindow) { this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs; this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs; @@ -1105,7 +1109,7 @@ export class WidgetSubscription implements IWidgetSubscription { this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000; if (this.timewindowForComparison.realtimeWindowMs) { this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf(); - this.comparisonTimeWindow.minTime = this.comparisonTimeWindow.maxTime - this.timewindowForComparison.realtimeWindowMs; + this.comparisonTimeWindow.minTime = moment(this.timeWindow.minTime).subtract(1, this.timeForComparison).valueOf(); } else if (this.timewindowForComparison.fixedWindow) { this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs; this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs; diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts index 7f0ccb90db..c6e0808d6a 100644 --- a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts @@ -50,7 +50,7 @@ export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor get intervals() { if (this.onlyCurrentInterval) { - return this.allIntervals.filter(interval => interval.startsWith('TODAY_') || interval.startsWith('CURRENT_')); + return this.allIntervals.filter(interval => interval.startsWith('TODAY') || interval.startsWith('CURRENT_')); } return this.allIntervals; } @@ -71,7 +71,6 @@ export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor writeValue(interval: QuickTimeInterval): void { this.modelValue = interval; - this.rendered = true; } onIntervalChange() { diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html index 432b84749a..d8d9681aeb 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html @@ -21,16 +21,43 @@ -
- -
+
+
+ + +
+
+
+ + +
+ timewindow.last + +
+
+ +
+ timewindow.interval + +
+
+
+
+
+
diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index b4aefef859..02a7f70b5c 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -19,7 +19,9 @@ import { aggregationTranslations, AggregationType, DAY, - HistoryWindowType, quickTimeIntervalPeriod, + HistoryWindowType, + quickTimeIntervalPeriod, + RealtimeWindowType, Timewindow, TimewindowType } from '@shared/models/time/time.models'; @@ -60,6 +62,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { historyTypes = HistoryWindowType; + realtimeTypes = RealtimeWindowType; + timewindowTypes = TimewindowType; aggregationTypes = AggregationType; @@ -89,6 +93,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { this.timewindowForm = this.fb.group({ realtime: this.fb.group( { + realtimeType: this.fb.control({ + value: this.timewindow.realtime && typeof this.timewindow.realtime.realtimeType !== 'undefined' + ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, + disabled: hideInterval + }), timewindowMs: [ this.timewindow.realtime && typeof this.timewindow.realtime.timewindowMs !== 'undefined' ? this.timewindow.realtime.timewindowMs : null @@ -96,7 +105,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { interval: [ this.timewindow.realtime && typeof this.timewindow.realtime.interval !== 'undefined' ? this.timewindow.realtime.interval : null - ] + ], + quickInterval: this.fb.control({ + value: this.timewindow.realtime && typeof this.timewindow.realtime.quickInterval !== 'undefined' + ? this.timewindow.realtime.quickInterval : null, + disabled: hideInterval + }) } ), history: this.fb.group( @@ -124,7 +138,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { value: this.timewindow.history && typeof this.timewindow.history.quickInterval !== 'undefined' ? this.timewindow.history.quickInterval : null, disabled: hideInterval - }), + }) } ), aggregation: this.fb.group( @@ -147,7 +161,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { update() { const timewindowFormValue = this.timewindowForm.getRawValue(); this.timewindow.realtime = { + realtimeType: timewindowFormValue.realtime.realtimeType, timewindowMs: timewindowFormValue.realtime.timewindowMs, + quickInterval: timewindowFormValue.realtime.quickInterval, interval: timewindowFormValue.realtime.interval }; this.timewindow.history = { @@ -180,11 +196,23 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { } minRealtimeAggInterval() { - return this.timeService.minIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value); + return this.timeService.minIntervalLimit(this.currentRealtimeTimewindow()); } maxRealtimeAggInterval() { - return this.timeService.maxIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value); + return this.timeService.maxIntervalLimit(this.currentRealtimeTimewindow()); + } + + currentRealtimeTimewindow(): number { + const timeWindowFormValue = this.timewindowForm.getRawValue(); + switch (timeWindowFormValue.realtime.realtimeType) { + case RealtimeWindowType.LAST_INTERVAL: + return timeWindowFormValue.realtime.timewindowMs; + case RealtimeWindowType.INTERVAL: + return quickTimeIntervalPeriod(timeWindowFormValue.realtime.quickInterval); + default: + return DAY; + } } minHistoryAggInterval() { @@ -215,11 +243,17 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { this.timewindowForm.get('history.timewindowMs').disable({emitEvent: false}); this.timewindowForm.get('history.fixedTimewindow').disable({emitEvent: false}); this.timewindowForm.get('history.quickInterval').disable({emitEvent: false}); + this.timewindowForm.get('realtime.realtimeType').disable({emitEvent: false}); + this.timewindowForm.get('realtime.timewindowMs').disable({emitEvent: false}); + this.timewindowForm.get('realtime.quickInterval').disable({emitEvent: false}); } else { this.timewindowForm.get('history.historyType').enable({emitEvent: false}); this.timewindowForm.get('history.timewindowMs').enable({emitEvent: false}); this.timewindowForm.get('history.fixedTimewindow').enable({emitEvent: false}); this.timewindowForm.get('history.quickInterval').enable({emitEvent: false}); + this.timewindowForm.get('realtime.realtimeType').enable({emitEvent: false}); + this.timewindowForm.get('realtime.timewindowMs').enable({emitEvent: false}); + this.timewindowForm.get('realtime.quickInterval').enable({emitEvent: false}); } this.timewindowForm.markAsDirty(); } diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index 59eada88a9..6ba81cdcfc 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -34,6 +34,7 @@ import { HistoryWindowType, initModelFromDefaultTimewindow, QuickTimeIntervalTranslationMap, + RealtimeWindowType, Timewindow, TimewindowType } from '@shared/models/time/time.models'; @@ -273,9 +274,13 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces updateDisplayValue() { if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) { - this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - ' + - this.translate.instant('timewindow.last-prefix') + ' ' + - this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs); + this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - '; + if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) { + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.realtime.quickInterval)); + } else { + this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' + + this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs); + } } else { this.innerValue.displayValue = !this.historyOnly ? (this.translate.instant('timewindow.history') + ' - ') : ''; if (this.innerValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index ee3748e214..bed6d6238d 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -15,7 +15,7 @@ /// import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isUndefined } from '@app/core/utils'; +import { deepClone, isDefined, isDefinedAndNotNull, isUndefined } from '@app/core/utils'; import * as moment_ from 'moment'; import { Observable } from 'rxjs/internal/Observable'; import { from, of } from 'rxjs'; @@ -35,6 +35,11 @@ export enum TimewindowType { HISTORY } +export enum RealtimeWindowType { + LAST_INTERVAL, + INTERVAL +} + export enum HistoryWindowType { LAST_INTERVAL, FIXED, @@ -47,6 +52,10 @@ export interface IntervalWindow { quickInterval?: QuickTimeInterval; } +export interface RealtimeWindow extends IntervalWindow{ + realtimeType?: RealtimeWindowType; +} + export interface FixedWindow { startTimeMs: number; endTimeMs: number; @@ -89,7 +98,7 @@ export interface Timewindow { hideAggregation?: boolean; hideAggInterval?: boolean; selectedTab?: TimewindowType; - realtime?: IntervalWindow; + realtime?: RealtimeWindow; history?: HistoryWindow; aggregation?: Aggregation; } @@ -102,6 +111,7 @@ export interface SubscriptionAggregation extends Aggregation { export interface SubscriptionTimewindow { startTs?: number; + quickInterval?: QuickTimeInterval; realtimeWindowMs?: number; fixedWindow?: FixedWindow; aggregation?: SubscriptionAggregation; @@ -168,8 +178,10 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { hideAggInterval: false, selectedTab: TimewindowType.REALTIME, realtime: { + realtimeType: RealtimeWindowType.LAST_INTERVAL, interval: SECOND, - timewindowMs: MINUTE + timewindowMs: MINUTE, + quickInterval: QuickTimeInterval.TODAY }, history: { historyType: HistoryWindowType.LAST_INTERVAL, @@ -208,7 +220,20 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T if (isDefined(value.realtime.interval)) { model.realtime.interval = value.realtime.interval; } - model.realtime.timewindowMs = value.realtime.timewindowMs; + if (isUndefined(value.realtime.realtimeType)) { + if (isDefined(value.realtime.quickInterval)) { + model.realtime.realtimeType = RealtimeWindowType.INTERVAL; + } else { + model.realtime.realtimeType = RealtimeWindowType.LAST_INTERVAL; + } + } else { + model.realtime.realtimeType = value.realtime.realtimeType; + } + if (model.realtime.realtimeType === RealtimeWindowType.INTERVAL) { + model.realtime.quickInterval = value.realtime.quickInterval; + } else { + model.realtime.timewindowMs = value.realtime.timewindowMs; + } } else { if (isDefined(value.history.interval)) { model.history.interval = value.history.interval; @@ -309,14 +334,27 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num selectedTab = isDefined(timewindow.realtime) ? TimewindowType.REALTIME : TimewindowType.HISTORY; } if (selectedTab === TimewindowType.REALTIME) { - subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs; + let realtimeType = timewindow.realtime.realtimeType; + if (isUndefined(realtimeType)) { + if (isDefined(timewindow.realtime.quickInterval)) { + realtimeType = RealtimeWindowType.INTERVAL; + } else { + realtimeType = RealtimeWindowType.LAST_INTERVAL; + } + } + if (realtimeType === RealtimeWindowType.INTERVAL) { + subscriptionTimewindow.realtimeWindowMs = getSubscriptionRealtimeWindowFromTimeInterval(timewindow.realtime.quickInterval); + subscriptionTimewindow.quickInterval = timewindow.realtime.quickInterval; + } else { + subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs; + } subscriptionTimewindow.aggregation.interval = timeService.boundIntervalToTimewindow(subscriptionTimewindow.realtimeWindowMs, timewindow.realtime.interval, subscriptionTimewindow.aggregation.type); subscriptionTimewindow.startTs = Date.now() + stDiff - subscriptionTimewindow.realtimeWindowMs; const startDiff = subscriptionTimewindow.startTs % subscriptionTimewindow.aggregation.interval; aggTimewindow = subscriptionTimewindow.realtimeWindowMs; - if (startDiff) { + if (startDiff && realtimeType !== RealtimeWindowType.INTERVAL) { subscriptionTimewindow.startTs -= startDiff; aggTimewindow += subscriptionTimewindow.aggregation.interval; } @@ -339,7 +377,11 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num }; aggTimewindow = timewindow.history.timewindowMs; } else if (historyType === HistoryWindowType.INTERVAL) { - subscriptionTimewindow.fixedWindow = createSubscriptionTimeWindowFromQuickKTimeInterval(timewindow.history.quickInterval); + const currentDate = moment(); + subscriptionTimewindow.fixedWindow = { + startTimeMs: calculateIntervalStartTime(timewindow.history.quickInterval, null, currentDate), + endTimeMs: calculateIntervalEndTime(timewindow.history.quickInterval, null, currentDate) + }; aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; } else { subscriptionTimewindow.fixedWindow = { @@ -360,77 +402,99 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num return subscriptionTimewindow; } -export function createSubscriptionTimeWindowFromQuickKTimeInterval(interval: QuickTimeInterval): FixedWindow { +function getSubscriptionRealtimeWindowFromTimeInterval(interval: QuickTimeInterval): number { const currentDate = moment(); - const timeWindow = { - startTimeMs: 0, - endTimeMs: 0 - }; + switch (interval) { + case QuickTimeInterval.TODAY: + case QuickTimeInterval.TODAY_SO_FAR: + return currentDate.diff(currentDate.clone().startOf('day')); + case QuickTimeInterval.CURRENT_WEEK: + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: + return currentDate.diff(currentDate.clone().startOf('week')); + case QuickTimeInterval.CURRENT_MONTH: + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: + return currentDate.diff(currentDate.clone().startOf('month')); + case QuickTimeInterval.CURRENT_YEAR: + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: + return currentDate.diff(currentDate.clone().startOf('year')); + } +} + +export function calculateIntervalEndTime(interval: QuickTimeInterval, endTs = 0, nowDate?: moment_.Moment): number { + const currentDate = isDefinedAndNotNull(nowDate) ? nowDate.clone() : moment(); + switch (interval) { + case QuickTimeInterval.YESTERDAY: + currentDate.subtract(1, 'days'); + return currentDate.endOf('day').valueOf(); + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: + currentDate.subtract(2, 'days'); + return currentDate.endOf('day').valueOf(); + case QuickTimeInterval.THIS_DAY_LAST_WEEK: + currentDate.subtract(1, 'weeks'); + return currentDate.endOf('day').valueOf(); + case QuickTimeInterval.PREVIOUS_WEEK: + currentDate.subtract(1, 'weeks'); + return currentDate.endOf('week').valueOf(); + case QuickTimeInterval.PREVIOUS_MONTH: + currentDate.subtract(1, 'months'); + return currentDate.endOf('month').valueOf(); + case QuickTimeInterval.PREVIOUS_YEAR: + currentDate.subtract(1, 'years'); + return currentDate.endOf('year').valueOf(); + case QuickTimeInterval.TODAY: + return currentDate.endOf('day').valueOf(); + case QuickTimeInterval.CURRENT_WEEK: + return currentDate.endOf('week').valueOf(); + case QuickTimeInterval.CURRENT_MONTH: + return currentDate.endOf('month').valueOf(); + case QuickTimeInterval.CURRENT_YEAR: + return currentDate.endOf('year').valueOf(); + case QuickTimeInterval.TODAY_SO_FAR: + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: + return currentDate.valueOf(); + default: + return endTs; + } +} + +export function calculateIntervalStartTime(interval: QuickTimeInterval, startTS = 0, nowDate?: moment_.Moment): number { + const currentDate = isDefinedAndNotNull(nowDate) ? nowDate.clone() : moment(); switch (interval) { case QuickTimeInterval.YESTERDAY: currentDate.subtract(1, 'days'); - timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); - break; + return currentDate.startOf('day').valueOf(); case QuickTimeInterval.DAY_BEFORE_YESTERDAY: currentDate.subtract(2, 'days'); - timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); - break; + return currentDate.startOf('day').valueOf(); case QuickTimeInterval.THIS_DAY_LAST_WEEK: currentDate.subtract(1, 'weeks'); - timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); - break; + return currentDate.startOf('day').valueOf(); case QuickTimeInterval.PREVIOUS_WEEK: currentDate.subtract(1, 'weeks'); - timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('week').valueOf(); - break; + return currentDate.startOf('week').valueOf(); case QuickTimeInterval.PREVIOUS_MONTH: currentDate.subtract(1, 'months'); - timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('month').valueOf(); - break; + return currentDate.startOf('month').valueOf(); case QuickTimeInterval.PREVIOUS_YEAR: currentDate.subtract(1, 'years'); - timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('year').valueOf(); - break; + return currentDate.startOf('year').valueOf(); case QuickTimeInterval.TODAY: - timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('day').valueOf(); - break; case QuickTimeInterval.TODAY_SO_FAR: - timeWindow.endTimeMs = currentDate.valueOf(); - timeWindow.startTimeMs = currentDate.startOf('day').valueOf(); - break; + return currentDate.startOf('day').valueOf(); case QuickTimeInterval.CURRENT_WEEK: - timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('week').valueOf(); - break; case QuickTimeInterval.CURRENT_WEEK_SO_FAR: - timeWindow.endTimeMs = currentDate.valueOf(); - timeWindow.startTimeMs = currentDate.startOf('week').valueOf(); - break; + return currentDate.startOf('week').valueOf(); case QuickTimeInterval.CURRENT_MONTH: - timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('month').valueOf(); - break; case QuickTimeInterval.CURRENT_MONTH_SO_FAR: - timeWindow.endTimeMs = currentDate.valueOf(); - timeWindow.startTimeMs = currentDate.startOf('month').valueOf(); - break; + return currentDate.startOf('month').valueOf(); case QuickTimeInterval.CURRENT_YEAR: - timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); - timeWindow.endTimeMs = currentDate.endOf('year').valueOf(); - break; case QuickTimeInterval.CURRENT_YEAR_SO_FAR: - timeWindow.endTimeMs = currentDate.valueOf(); - timeWindow.startTimeMs = currentDate.startOf('year').valueOf(); - break; + return currentDate.startOf('year').valueOf(); + default: + return startTS; } - return timeWindow; } export function quickTimeIntervalPeriod(interval: QuickTimeInterval): number { From 8f8613db99c26f639f011e672fc11e23257375e5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 16 Mar 2021 14:55:56 +0200 Subject: [PATCH 3/4] UI: Refactoring; Add current hour --- .../time/quick-time-interval.component.ts | 2 +- .../src/app/shared/models/time/time.models.ts | 38 ++++++++++++------- .../assets/locale/locale.constant-en_US.json | 5 ++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts index c6e0808d6a..3eab8c428d 100644 --- a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.ts @@ -50,7 +50,7 @@ export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor get intervals() { if (this.onlyCurrentInterval) { - return this.allIntervals.filter(interval => interval.startsWith('TODAY') || interval.startsWith('CURRENT_')); + return this.allIntervals.filter(interval => interval.startsWith('CURRENT_')); } return this.allIntervals; } diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index bed6d6238d..dd057eba03 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -131,8 +131,9 @@ export enum QuickTimeInterval { PREVIOUS_WEEK = 'PREVIOUS_WEEK', PREVIOUS_MONTH = 'PREVIOUS_MONTH', PREVIOUS_YEAR = 'PREVIOUS_YEAR', - TODAY = 'TODAY', - TODAY_SO_FAR = 'TODAY_SO_FAR', + CURRENT_HOUR = 'CURRENT_HOUR', + CURRENT_DAY = 'CURRENT_DAY', + CURRENT_DAY_SO_FAR = 'CURRENT_DAY_SO_FAR', CURRENT_WEEK = 'CURRENT_WEEK', CURRENT_WEEK_SO_FAR = 'CURRENT_WEEK_SO_WAR', CURRENT_MONTH = 'CURRENT_MONTH', @@ -148,8 +149,9 @@ export const QuickTimeIntervalTranslationMap = new Map Date: Wed, 17 Mar 2021 18:38:57 +0200 Subject: [PATCH 4/4] Add timezone support for timewindow --- ui-ngx/src/app/core/api/data-aggregator.ts | 79 +++--- .../app/core/api/entity-data-subscription.ts | 39 ++- .../src/app/core/api/widget-subscription.ts | 30 ++- .../dashboard-page.component.html | 1 + .../dashboard/dashboard.component.html | 1 + .../entity/entities-table.component.ts | 12 +- .../profile/alarm/alarm-schedule.component.ts | 9 +- .../time/timewindow-panel.component.html | 14 +- .../time/timewindow-panel.component.ts | 26 +- .../components/time/timewindow.component.ts | 14 +- .../time/timezone-select.component.ts | 57 ++-- .../models/telemetry/telemetry.models.ts | 41 +++ .../src/app/shared/models/time/time.models.ts | 250 +++++++++--------- 13 files changed, 340 insertions(+), 233 deletions(-) diff --git a/ui-ngx/src/app/core/api/data-aggregator.ts b/ui-ngx/src/app/core/api/data-aggregator.ts index e0ebb3cbc1..e31d821efd 100644 --- a/ui-ngx/src/app/core/api/data-aggregator.ts +++ b/ui-ngx/src/app/core/api/data-aggregator.ts @@ -18,8 +18,8 @@ import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/tel import { AggregationType, calculateIntervalEndTime, - calculateIntervalStartTime, - QuickTimeInterval + calculateIntervalStartTime, getCurrentTime, + QuickTimeInterval, SubscriptionTimewindow } from '@shared/models/time/time.models'; import { UtilsService } from '@core/services/utils.service'; import { deepClone } from '@core/utils'; @@ -78,34 +78,29 @@ export class DataAggregator { private resetPending = false; private updatedData = false; - private noAggregation = this.aggregationType === AggregationType.NONE; - private aggregationTimeout = Math.max(this.interval, 1000); + private noAggregation = this.subsTw.aggregation.type === AggregationType.NONE; + private aggregationTimeout = Math.max(this.subsTw.aggregation.interval, 1000); private readonly aggFunction: AggFunction; private intervalTimeoutHandle: Timeout; private intervalScheduledTime: number; + private startTs = this.subsTw.startTs + this.subsTw.tsOffset; private endTs: number; private elapsed: number; constructor(private onDataCb: onAggregatedData, private tsKeyNames: string[], - private startTs: number, - private limit: number, - private aggregationType: AggregationType, - private timeWindow: number, - private interval: number, - private stateData: boolean, + private subsTw: SubscriptionTimewindow, private utils: UtilsService, - private ignoreDataUpdateOnIntervalTick: boolean, - private quickInterval: QuickTimeInterval) { + private ignoreDataUpdateOnIntervalTick: boolean) { this.tsKeyNames.forEach((key) => { this.dataBuffer[key] = []; }); - if (this.stateData) { + if (this.subsTw.aggregation.stateData) { this.lastPrevKvPairData = {}; } - switch (this.aggregationType) { + switch (this.subsTw.aggregation.type) { case AggregationType.MIN: this.aggFunction = min; break; @@ -135,19 +130,21 @@ export class DataAggregator { return prevOnDataCb; } - public reset(startTs: number, timeWindow: number, interval: number) { + public reset(subsTw: SubscriptionTimewindow) { if (this.intervalTimeoutHandle) { clearTimeout(this.intervalTimeoutHandle); this.intervalTimeoutHandle = null; } + this.subsTw = subsTw; this.intervalScheduledTime = this.utils.currentPerfTime(); - this.startTs = startTs; - this.timeWindow = timeWindow; - this.interval = interval; - const endTs = this.startTs + this.timeWindow; - this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); + this.startTs = this.subsTw.startTs + this.subsTw.tsOffset; + if (this.subsTw.quickInterval) { + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, null, this.subsTw.timezone) + this.subsTw.tsOffset; + } else { + this.endTs = this.startTs + this.subsTw.aggregation.timeWindow; + } this.elapsed = 0; - this.aggregationTimeout = Math.max(this.interval, 1000); + this.aggregationTimeout = Math.max(this.subsTw.aggregation.interval, 1000); this.resetPending = true; this.updatedData = false; this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), this.aggregationTimeout); @@ -168,8 +165,11 @@ export class DataAggregator { if (!this.dataReceived) { this.elapsed = 0; this.dataReceived = true; - const endTs = this.startTs + this.timeWindow; - this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); + if (this.subsTw.quickInterval) { + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, null, this.subsTw.timezone) + this.subsTw.tsOffset; + } else { + this.endTs = this.startTs + this.subsTw.aggregation.timeWindow; + } } if (this.resetPending) { this.resetPending = false; @@ -203,15 +203,19 @@ export class DataAggregator { this.intervalTimeoutHandle = null; } if (!history) { - const delta = Math.floor(this.elapsed / this.interval); + const delta = Math.floor(this.elapsed / this.subsTw.aggregation.interval); if (delta || !this.data) { - const tickTs = delta * this.interval; - const startTS = this.startTs + tickTs; - this.startTs = calculateIntervalStartTime(this.quickInterval, startTS); - const endTs = this.endTs + tickTs; - this.endTs = calculateIntervalEndTime(this.quickInterval, endTs); + const tickTs = delta * this.subsTw.aggregation.interval; + if (this.subsTw.quickInterval) { + const currentDate = getCurrentTime(this.subsTw.timezone); + this.startTs = calculateIntervalStartTime(this.subsTw.quickInterval, currentDate) + this.subsTw.tsOffset; + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, currentDate) + this.subsTw.tsOffset; + } else { + this.startTs += tickTs; + this.endTs += tickTs; + } this.data = this.updateData(); - this.elapsed = this.elapsed - delta * this.interval; + this.elapsed = this.elapsed - delta * this.subsTw.aggregation.interval; } } else { this.data = this.updateData(); @@ -234,7 +238,7 @@ export class DataAggregator { let keyData = this.dataBuffer[key]; aggKeyData.forEach((aggData, aggTimestamp) => { if (aggTimestamp <= this.startTs) { - if (this.stateData && + if (this.subsTw.aggregation.stateData && (!this.lastPrevKvPairData[key] || this.lastPrevKvPairData[key][0] < aggTimestamp)) { this.lastPrevKvPairData[key] = [aggTimestamp, aggData.aggValue]; } @@ -246,11 +250,11 @@ export class DataAggregator { } }); keyData.sort((set1, set2) => set1[0] - set2[0]); - if (this.stateData) { + if (this.subsTw.aggregation.stateData) { this.updateStateBounds(keyData, deepClone(this.lastPrevKvPairData[key])); } - if (keyData.length > this.limit) { - keyData = keyData.slice(keyData.length - this.limit); + if (keyData.length > this.subsTw.aggregation.limit) { + keyData = keyData.slice(keyData.length - this.subsTw.aggregation.limit); } this.dataBuffer[key] = keyData; } @@ -286,7 +290,7 @@ export class DataAggregator { } private processAggregatedData(data: SubscriptionData): AggregationMap { - const isCount = this.aggregationType === AggregationType.COUNT; + const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; const aggregationMap: AggregationMap = {}; for (const key of Object.keys(data)) { let aggKeyData = aggregationMap[key]; @@ -311,7 +315,7 @@ export class DataAggregator { } private updateAggregatedData(data: SubscriptionData) { - const isCount = this.aggregationType === AggregationType.COUNT; + const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; for (const key of Object.keys(data)) { let aggKeyData = this.aggregationMap[key]; if (!aggKeyData) { @@ -323,7 +327,8 @@ export class DataAggregator { const timestamp = kvPair[0]; const value = this.convertValue(kvPair[1]); const aggTimestamp = this.noAggregation ? timestamp : (this.startTs + - Math.floor((timestamp - this.startTs) / this.interval) * this.interval + this.interval / 2); + Math.floor((timestamp - this.startTs) / this.subsTw.aggregation.interval) * + this.subsTw.aggregation.interval + this.subsTw.aggregation.interval / 2); let aggData = aggKeyData.get(aggTimestamp); if (!aggData) { aggData = { diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index 6dc8036df6..1b8c99a285 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -237,7 +237,7 @@ export class EntityDataSubscription { }; if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { - this.prepareSubscriptionCommands(this.dataCommand); + this.prepareSubscriptionCommands(); } this.subscriber.subscriptionCommands.push(this.dataCommand); @@ -256,8 +256,8 @@ export class EntityDataSubscription { if (this.started) { const targetCommand = this.entityDataSubscriptionOptions.isPaginatedDataSubscription ? this.dataCommand : this.subsCommand; if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && - !this.history && this.tsFields.length) { - const newSubsTw: SubscriptionTimewindow = this.listener.updateRealtimeSubscription(); + !this.history && this.tsFields.length) { + const newSubsTw = this.listener.updateRealtimeSubscription(); this.subsTw = newSubsTw; targetCommand.tsCmd.startTs = this.subsTw.startTs; targetCommand.tsCmd.timeWindow = this.subsTw.aggregation.timeWindow; @@ -266,9 +266,10 @@ export class EntityDataSubscription { targetCommand.tsCmd.agg = this.subsTw.aggregation.type; targetCommand.tsCmd.fetchLatestPreviousPoint = this.subsTw.aggregation.stateData; this.dataAggregators.forEach((dataAggregator) => { - dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); + dataAggregator.reset(newSubsTw); }); } + this.subscriber.setTsOffset(this.subsTw.tsOffset); targetCommand.query = this.dataCommand.query; this.subscriber.subscriptionCommands = [targetCommand]; } else { @@ -393,7 +394,7 @@ export class EntityDataSubscription { if (this.datasourceType === DatasourceType.entity) { this.subsCommand = new EntityDataCmd(); this.subsCommand.cmdId = this.dataCommand.cmdId; - this.prepareSubscriptionCommands(this.subsCommand); + this.prepareSubscriptionCommands(); if (!this.subsCommand.isEmpty()) { this.subscriber.subscriptionCommands = [this.subsCommand]; this.subscriber.update(); @@ -404,11 +405,11 @@ export class EntityDataSubscription { this.started = true; } - private prepareSubscriptionCommands(cmd: EntityDataCmd) { + private prepareSubscriptionCommands() { if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) { if (this.tsFields.length > 0) { if (this.history) { - cmd.historyCmd = { + this.subsCommand.historyCmd = { keys: this.tsFields.map(key => key.key), startTs: this.subsTw.fixedWindow.startTimeMs, endTs: this.subsTw.fixedWindow.endTimeMs, @@ -418,7 +419,7 @@ export class EntityDataSubscription { fetchLatestPreviousPoint: this.subsTw.aggregation.stateData }; } else { - cmd.tsCmd = { + this.subsCommand.tsCmd = { keys: this.tsFields.map(key => key.key), startTs: this.subsTw.startTs, timeWindow: this.subsTw.aggregation.timeWindow, @@ -429,9 +430,10 @@ export class EntityDataSubscription { }; } } + this.subscriber.setTsOffset(this.subsTw.tsOffset); } else if (this.entityDataSubscriptionOptions.type === widgetType.latest) { if (this.latestValues.length > 0) { - cmd.latestCmd = { + this.subsCommand.latestCmd = { keys: this.latestValues }; } @@ -745,15 +747,9 @@ export class EntityDataSubscription { this.onData(data, dataKeyType, dataIndex, detectChanges, dataUpdatedCb); }, tsKeyNames, - subsTw.startTs, - subsTw.aggregation.limit, - subsTw.aggregation.type, - subsTw.aggregation.timeWindow, - subsTw.aggregation.interval, - subsTw.aggregation.stateData, + subsTw, this.utils, - this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick, - subsTw.quickInterval + this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick ); } @@ -828,7 +824,8 @@ export class EntityDataSubscription { startTime = dataKey.lastUpdateTime + this.frequency; endTime = dataKey.lastUpdateTime + deltaElapsed; } else { - startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.startTs; + startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.startTs + + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; endTime = startTime + this.entityDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs + this.frequency; if (this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.type === AggregationType.NONE) { const time = endTime - this.frequency * this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.limit; @@ -836,8 +833,10 @@ export class EntityDataSubscription { } } } else { - startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.startTimeMs; - endTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.endTimeMs; + startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.startTimeMs + + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; + endTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.endTimeMs + + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; } } generatedData.data[`${dataKey.name}_${dataKey.index}`] = this.generateSeries(dataKey, index, startTime, endTime); diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index d33329f1cc..e658618dea 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -40,7 +40,7 @@ import { calculateIntervalEndTime, calculateIntervalStartTime, createSubscriptionTimewindow, - createTimewindowForComparison, + createTimewindowForComparison, getCurrentTime, SubscriptionTimewindow, Timewindow, toHistoryTimewindow, @@ -839,9 +839,11 @@ export class WidgetSubscription implements IWidgetSubscription { if (this.alarmDataListener) { this.ctx.alarmDataService.stopSubscription(this.alarmDataListener); } + if (this.timeWindowConfig) { this.updateRealtimeSubscription(); } + this.alarmDataListener = { subscriptionTimewindow: this.subscriptionTimewindow, alarmSource: this.alarmSource, @@ -1082,14 +1084,21 @@ export class WidgetSubscription implements IWidgetSubscription { private updateTimewindow() { this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000; + this.timeWindow.timezone = this.subscriptionTimewindow.timezone; if (this.subscriptionTimewindow.realtimeWindowMs) { - this.timeWindow.maxTime = calculateIntervalEndTime( - this.subscriptionTimewindow.quickInterval, moment().valueOf() + this.timeWindow.stDiff); - this.timeWindow.minTime = calculateIntervalStartTime( - this.subscriptionTimewindow.quickInterval, this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs); + if (this.subscriptionTimewindow.quickInterval) { + const currentDate = getCurrentTime(this.subscriptionTimewindow.timezone); + this.timeWindow.maxTime = calculateIntervalEndTime( + this.subscriptionTimewindow.quickInterval, currentDate) + this.subscriptionTimewindow.tsOffset; + this.timeWindow.minTime = calculateIntervalStartTime( + this.subscriptionTimewindow.quickInterval, currentDate) + this.subscriptionTimewindow.tsOffset; + } else { + this.timeWindow.maxTime = moment().valueOf() + this.subscriptionTimewindow.tsOffset + this.timeWindow.stDiff; + this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; + } } else if (this.subscriptionTimewindow.fixedWindow) { - this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs; - this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs; + this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs + this.subscriptionTimewindow.tsOffset; + this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs + this.subscriptionTimewindow.tsOffset; } } @@ -1107,12 +1116,13 @@ export class WidgetSubscription implements IWidgetSubscription { private updateComparisonTimewindow() { this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000; + this.comparisonTimeWindow.timezone = this.timewindowForComparison.timezone; if (this.timewindowForComparison.realtimeWindowMs) { this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf(); this.comparisonTimeWindow.minTime = moment(this.timeWindow.minTime).subtract(1, this.timeForComparison).valueOf(); } else if (this.timewindowForComparison.fixedWindow) { - this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs; - this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs; + this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs + this.timewindowForComparison.tsOffset; + this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs + this.timewindowForComparison.tsOffset; } } @@ -1339,7 +1349,7 @@ export class WidgetSubscription implements IWidgetSubscription { this.onDataUpdated(); } - private alarmsUpdated(_updated: Array, alarms: PageData) { + private alarmsUpdated(updated: Array, alarms: PageData) { this.alarmsLoaded(alarms, 0, 0); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html index 1cc501a0d1..762b7149a2 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html @@ -91,6 +91,7 @@ direction="left" tooltipPosition="below" aggregation="true" + timezone="true" [(ngModel)]="dashboardCtx.dashboardTimewindow"> diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index da735c1567..c1dd69ecf5 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -55,7 +55,13 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; import { DialogService } from '@core/services/dialog.service'; import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; -import { HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; +import { + calculateIntervalEndTime, + calculateIntervalStartTime, + getCurrentTime, + HistoryWindowType, + Timewindow +} from '@shared/models/time/time.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isUndefined } from '@core/utils'; @@ -296,6 +302,10 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn const currentTime = Date.now(); timePageLink.startTime = currentTime - this.timewindow.history.timewindowMs; timePageLink.endTime = currentTime; + } else if (this.timewindow.history.historyType === HistoryWindowType.INTERVAL) { + const currentDate = getCurrentTime(); + timePageLink.startTime = calculateIntervalStartTime(this.timewindow.history.quickInterval, currentDate); + timePageLink.endTime = calculateIntervalEndTime(this.timewindow.history.quickInterval, currentDate); } else { timePageLink.startTime = this.timewindow.history.fixedTimewindow.startTimeMs; timePageLink.endTime = this.timewindow.history.fixedTimewindow.endTimeMs; diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts index 5c9a1cd0aa..d5c9f02876 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts @@ -94,11 +94,10 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems) }); this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { - getDefaultTimezone().subscribe((defaultTimezone) => { - this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); - this.updateValidators(type, true); - this.alarmScheduleForm.updateValueAndValidity(); - }); + const defaultTimezone = getDefaultTimezone(); + this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); + this.updateValidators(type, true); + this.alarmScheduleForm.updateValueAndValidity(); }); this.alarmScheduleForm.valueChanges.subscribe(() => { this.updateModel(); diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html index d8d9681aeb..42c3266e62 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html @@ -49,7 +49,7 @@ formControlName="quickInterval" onlyCurrentInterval="true" [fxShow]="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL" - [required]="timewindow.selectedTab === timewindowTypes.HISTORY && + [required]="timewindow.selectedTab === timewindowTypes.REALTIME && timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL" style="padding-top: 8px; min-width: 364px">
@@ -99,7 +99,7 @@ formControlName="quickInterval" [fxShow]="timewindowForm.get('history.historyType').value === historyTypes.INTERVAL" [required]="timewindow.selectedTab === timewindowTypes.HISTORY && - timewindowForm.get('history.historyType').value === historyTypes.INTERVAL" + timewindowForm.get('history.historyType').value === historyTypes.INTERVAL" style="padding-top: 8px; min-width: 364px"> @@ -177,6 +177,16 @@ predefinedName="aggregation.group-interval"> +
+
+ + +
+ + +