diff --git a/ui-ngx/src/app/core/api/alarm-data-subscription.ts b/ui-ngx/src/app/core/api/alarm-data-subscription.ts index c7db10025d..7a8df1f704 100644 --- a/ui-ngx/src/app/core/api/alarm-data-subscription.ts +++ b/ui-ngx/src/app/core/api/alarm-data-subscription.ts @@ -130,6 +130,7 @@ export class AlarmDataSubscription { this.alarmDataCommand.query.pageLink.timeWindow = this.subsTw.realtimeWindowMs; } + this.subscriber.setTsOffset(this.subsTw.tsOffset); this.subscriber.subscriptionCommands.push(this.alarmDataCommand); this.subscriber.alarmData$.subscribe((alarmDataUpdate) => { @@ -143,8 +144,11 @@ export class AlarmDataSubscription { this.subscriber.subscribe(); } else if (this.datasourceType === DatasourceType.function) { + const alarm = deepClone(simulatedAlarm); + alarm.createdTime += this.subsTw.tsOffset; + alarm.startTs += this.subsTw.tsOffset; const pageData: PageData = { - data: [{...simulatedAlarm, entityId: '1', latest: {}}], + data: [{...alarm, entityId: '1', latest: {}}], hasNext: false, totalElements: 1, totalPages: 1 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 1b8c99a285..7a26a5373d 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -15,7 +15,7 @@ /// import { DataSet, DataSetHolder, DatasourceType, widgetType } from '@shared/models/widget.models'; -import { AggregationType, SubscriptionTimewindow } from '@shared/models/time/time.models'; +import { AggregationType, getCurrentTime, SubscriptionTimewindow } from '@shared/models/time/time.models'; import { EntityData, EntityDataPageLink, @@ -74,6 +74,7 @@ export interface EntityDataSubscriptionOptions { keyFilters?: Array; additionalKeyFilters?: Array; subscriptionTimewindow?: SubscriptionTimewindow; + latestTsOffset?: number; } export class EntityDataSubscription { @@ -95,6 +96,7 @@ export class EntityDataSubscription { private entityDataResolveSubject: Subject; private pageData: PageData; private subsTw: SubscriptionTimewindow; + private latestTsOffset: number; private dataAggregators: Array; private dataKeys: {[key: string]: Array | SubscriptionDataKey} = {}; private datasourceData: {[index: number]: {[key: string]: DataSetHolder}}; @@ -177,6 +179,7 @@ export class EntityDataSubscription { this.started = true; this.dataResolved = true; this.subsTw = this.entityDataSubscriptionOptions.subscriptionTimewindow; + this.latestTsOffset = this.entityDataSubscriptionOptions.latestTsOffset; this.history = this.entityDataSubscriptionOptions.subscriptionTimewindow && isObject(this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow); this.realtime = this.entityDataSubscriptionOptions.subscriptionTimewindow && @@ -237,7 +240,12 @@ export class EntityDataSubscription { }; if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { - this.prepareSubscriptionCommands(); + this.prepareSubscriptionCommands(this.dataCommand); + if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) { + this.subscriber.setTsOffset(this.subsTw.tsOffset); + } else { + this.subscriber.setTsOffset(this.latestTsOffset); + } } this.subscriber.subscriptionCommands.push(this.dataCommand); @@ -276,9 +284,15 @@ export class EntityDataSubscription { this.subscriber.subscriptionCommands = [this.dataCommand]; } }); - this.subscriber.subscribe(); } else if (this.datasourceType === DatasourceType.function) { + let tsOffset = 0; + if (this.entityDataSubscriptionOptions.type === widgetType.latest) { + tsOffset = this.entityDataSubscriptionOptions.latestTsOffset; + } else if (this.entityDataSubscriptionOptions.subscriptionTimewindow) { + tsOffset = this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; + } + const entityData: EntityData = { entityId: { id: NULL_UUID, @@ -289,7 +303,7 @@ export class EntityDataSubscription { }; const name = DatasourceType.function; entityData.latest[EntityKeyType.ENTITY_FIELD] = { - name: {ts: Date.now(), value: name} + name: {ts: Date.now() + tsOffset, value: name} }; const pageData: PageData = { data: [entityData], @@ -299,7 +313,9 @@ export class EntityDataSubscription { }; this.onPageData(pageData); } else if (this.datasourceType === DatasourceType.entityCount) { + this.latestTsOffset = this.entityDataSubscriptionOptions.latestTsOffset; this.subscriber = new TelemetrySubscriber(this.telemetryService); + this.subscriber.setTsOffset(this.latestTsOffset); this.countCommand = new EntityCountCmd(); let keyFilters = this.entityDataSubscriptionOptions.keyFilters; if (this.entityDataSubscriptionOptions.additionalKeyFilters) { @@ -332,13 +348,13 @@ export class EntityDataSubscription { latest: { [EntityKeyType.ENTITY_FIELD]: { name: { - ts: Date.now(), + ts: Date.now() + this.latestTsOffset, value: DatasourceType.entityCount } }, [EntityKeyType.COUNT]: { [countKey.name]: { - ts: Date.now(), + ts: Date.now() + this.latestTsOffset, value: entityCountUpdate.count + '' } } @@ -359,7 +375,7 @@ export class EntityDataSubscription { latest: { [EntityKeyType.COUNT]: { [countKey.name]: { - ts: Date.now(), + ts: Date.now() + this.latestTsOffset, value: entityCountUpdate.count + '' } } @@ -384,6 +400,7 @@ export class EntityDataSubscription { return; } this.subsTw = this.entityDataSubscriptionOptions.subscriptionTimewindow; + this.latestTsOffset = this.entityDataSubscriptionOptions.latestTsOffset; this.history = this.entityDataSubscriptionOptions.subscriptionTimewindow && isObject(this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow); this.realtime = this.entityDataSubscriptionOptions.subscriptionTimewindow && @@ -394,22 +411,38 @@ export class EntityDataSubscription { if (this.datasourceType === DatasourceType.entity) { this.subsCommand = new EntityDataCmd(); this.subsCommand.cmdId = this.dataCommand.cmdId; - this.prepareSubscriptionCommands(); - if (!this.subsCommand.isEmpty()) { + this.prepareSubscriptionCommands(this.subsCommand); + let latestTsOffsetChanged = false; + if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) { + this.subscriber.setTsOffset(this.subsTw.tsOffset); + } else { + latestTsOffsetChanged = this.subscriber.setTsOffset(this.latestTsOffset); + } + if (latestTsOffsetChanged) { + if (this.listener.initialPageDataChanged) { + this.listener.initialPageDataChanged(this.pageData); + } + } else if (!this.subsCommand.isEmpty()) { this.subscriber.subscriptionCommands = [this.subsCommand]; this.subscriber.update(); } + } else if (this.datasourceType === DatasourceType.entityCount) { + if (this.subscriber.setTsOffset(this.latestTsOffset)) { + if (this.listener.initialPageDataChanged) { + this.listener.initialPageDataChanged(this.pageData); + } + } } else if (this.datasourceType === DatasourceType.function) { this.startFunction(); } this.started = true; } - private prepareSubscriptionCommands() { + private prepareSubscriptionCommands(cmd: EntityDataCmd) { if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) { if (this.tsFields.length > 0) { if (this.history) { - this.subsCommand.historyCmd = { + cmd.historyCmd = { keys: this.tsFields.map(key => key.key), startTs: this.subsTw.fixedWindow.startTimeMs, endTs: this.subsTw.fixedWindow.endTimeMs, @@ -419,7 +452,7 @@ export class EntityDataSubscription { fetchLatestPreviousPoint: this.subsTw.aggregation.stateData }; } else { - this.subsCommand.tsCmd = { + cmd.tsCmd = { keys: this.tsFields.map(key => key.key), startTs: this.subsTw.startTs, timeWindow: this.subsTw.aggregation.timeWindow, @@ -430,10 +463,9 @@ export class EntityDataSubscription { }; } } - this.subscriber.setTsOffset(this.subsTw.tsOffset); } else if (this.entityDataSubscriptionOptions.type === widgetType.latest) { if (this.latestValues.length > 0) { - this.subsCommand.latestCmd = { + cmd.latestCmd = { keys: this.latestValues }; } @@ -783,7 +815,7 @@ export class EntityDataSubscription { } else { prevSeries = [0, 0]; } - const time = Date.now(); + const time = Date.now() + this.latestTsOffset; const value = dataKey.func(time, prevSeries[1]); const series: [number, any] = [time, value]; this.datasourceData[0][dataKey.key].data = [series]; @@ -838,6 +870,10 @@ export class EntityDataSubscription { endTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.endTimeMs + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; } + if (this.entityDataSubscriptionOptions.subscriptionTimewindow.quickInterval) { + const currentTime = getCurrentTime().valueOf() + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; + endTime = Math.min(currentTime, endTime); + } } generatedData.data[`${dataKey.name}_${dataKey.index}`] = this.generateSeries(dataKey, index, startTime, endTime); } diff --git a/ui-ngx/src/app/core/api/entity-data.service.ts b/ui-ngx/src/app/core/api/entity-data.service.ts index d9df28baba..7f45cee73f 100644 --- a/ui-ngx/src/app/core/api/entity-data.service.ts +++ b/ui-ngx/src/app/core/api/entity-data.service.ts @@ -32,6 +32,7 @@ import { Observable, of } from 'rxjs'; export interface EntityDataListener { subscriptionType: widgetType; subscriptionTimewindow?: SubscriptionTimewindow; + latestTsOffset?: number; configDatasource: Datasource; configDatasourceIndex: number; dataLoaded: (pageData: PageData, @@ -92,6 +93,8 @@ export class EntityDataService { if (listener.subscription) { if (listener.subscriptionType === widgetType.timeseries) { listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow); + } else if (listener.subscriptionType === widgetType.latest) { + listener.subscriptionOptions.latestTsOffset = listener.latestTsOffset; } listener.subscription.start(); } @@ -118,6 +121,8 @@ export class EntityDataService { listener.subscription = new EntityDataSubscription(listener, this.telemetryService, this.utils); if (listener.subscriptionType === widgetType.timeseries) { listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow); + } else if (listener.subscriptionType === widgetType.latest) { + listener.subscriptionOptions.latestTsOffset = listener.latestTsOffset; } return listener.subscription.subscribe(); } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index e658618dea..ba79e8f09a 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -39,8 +39,10 @@ import { HttpErrorResponse } from '@angular/common/http'; import { calculateIntervalEndTime, calculateIntervalStartTime, + calculateTsOffset, createSubscriptionTimewindow, - createTimewindowForComparison, getCurrentTime, + createTimewindowForComparison, + getCurrentTime, SubscriptionTimewindow, Timewindow, toHistoryTimewindow, @@ -79,8 +81,10 @@ export class WidgetSubscription implements IWidgetSubscription { timeWindow: WidgetTimewindow; originalTimewindow: Timewindow; timeWindowConfig: Timewindow; + timezone: string; subscriptionTimewindow: SubscriptionTimewindow; useDashboardTimewindow: boolean; + tsOffset = 0; hasDataPageLink: boolean; singleEntity: boolean; @@ -213,6 +217,10 @@ export class WidgetSubscription implements IWidgetSubscription { this.timeWindow = {}; this.useDashboardTimewindow = options.useDashboardTimewindow; this.stateData = options.stateData; + if (this.type === widgetType.latest) { + this.timezone = options.dashboardTimewindow.timezone; + this.updateTsOffset(); + } if (this.useDashboardTimewindow) { this.timeWindowConfig = deepClone(options.dashboardTimewindow); } else { @@ -578,11 +586,16 @@ export class WidgetSubscription implements IWidgetSubscription { if (!isEqual(this.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { this.timeWindowConfig = deepClone(newDashboardTimewindow); this.update(); - return true; + } + } + } else if (this.type === widgetType.latest) { + if (newDashboardTimewindow && this.timezone !== newDashboardTimewindow.timezone) { + this.timezone = newDashboardTimewindow.timezone; + if (this.updateTsOffset()) { + this.update(); } } } - return false; } updateDataVisibility(index: number): void { @@ -815,6 +828,7 @@ export class WidgetSubscription implements IWidgetSubscription { configDatasource: datasource, configDatasourceIndex: datasourceIndex, subscriptionTimewindow: this.subscriptionTimewindow, + latestTsOffset: this.tsOffset, dataLoaded: (pageData, data1, datasourceIndex1, pageLink1) => { this.dataLoaded(pageData, data1, datasourceIndex1, pageLink1, true); }, @@ -882,25 +896,28 @@ export class WidgetSubscription implements IWidgetSubscription { } private dataSubscribe() { + this.updateDataTimewindow(); if (!this.hasDataPageLink) { - if (this.type === widgetType.timeseries && this.timeWindowConfig) { - this.updateDataTimewindow(); - if (this.subscriptionTimewindow.fixedWindow) { + if (this.type === widgetType.timeseries && this.timeWindowConfig && this.subscriptionTimewindow.fixedWindow) { this.onDataUpdated(); - } } const forceUpdate = !this.datasources.length; + const notifyDataLoaded = !this.entityDataListeners.filter((listener) => listener.subscription ? true : false).length; this.entityDataListeners.forEach((listener) => { if (this.comparisonEnabled && listener.configDatasource.isAdditional) { listener.subscriptionTimewindow = this.timewindowForComparison; } else { listener.subscriptionTimewindow = this.subscriptionTimewindow; + listener.latestTsOffset = this.tsOffset; } this.ctx.entityDataService.startSubscription(listener); }); if (forceUpdate) { this.onDataUpdated(); } + if (notifyDataLoaded) { + this.notifyDataLoaded(); + } } } @@ -1102,6 +1119,15 @@ export class WidgetSubscription implements IWidgetSubscription { } } + private updateTsOffset(): boolean { + const newOffset = calculateTsOffset(this.timezone); + if (this.tsOffset !== newOffset) { + this.tsOffset = newOffset; + return true; + } + return false; + } + private updateRealtimeSubscription(subscriptionTimewindow?: SubscriptionTimewindow): SubscriptionTimewindow { if (subscriptionTimewindow) { this.subscriptionTimewindow = subscriptionTimewindow; 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 42c3266e62..57c596bbc2 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 @@ -133,7 +133,7 @@ -
+
aggregation.limit @@ -184,7 +184,8 @@ (ngModelChange)="onHideTimezoneChanged()">
+ localBrowserTimezonePlaceholderOnEmpty="true" + formControlName="timezone">
diff --git a/ui-ngx/src/app/shared/components/time/timezone-select.component.ts b/ui-ngx/src/app/shared/components/time/timezone-select.component.ts index e5ea8b9611..cbc9ef1c92 100644 --- a/ui-ngx/src/app/shared/components/time/timezone-select.component.ts +++ b/ui-ngx/src/app/shared/components/time/timezone-select.component.ts @@ -23,7 +23,8 @@ import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { getTimezoneInfo, getTimezones, TimezoneInfo } from '@shared/models/time/time.models'; +import { getDefaultTimezoneInfo, getTimezoneInfo, getTimezones, TimezoneInfo } from '@shared/models/time/time.models'; +import { deepClone } from '@core/utils'; @Component({ selector: 'tb-timezone-select', @@ -68,6 +69,15 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af this.userTimezoneByDefaultValue = coerceBooleanProperty(value); } + private localBrowserTimezonePlaceholderOnEmptyValue: boolean; + get localBrowserTimezonePlaceholderOnEmpty(): boolean { + return this.localBrowserTimezonePlaceholderOnEmptyValue; + } + @Input() + set localBrowserTimezonePlaceholderOnEmpty(value: boolean) { + this.localBrowserTimezonePlaceholderOnEmptyValue = coerceBooleanProperty(value); + } + @Input() disabled: boolean; @@ -81,6 +91,10 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af private dirty = false; + private localBrowserTimezoneInfoPlaceholder: TimezoneInfo; + + private timezones: Array; + private propagateChange = (v: any) => { }; constructor(private store: Store, @@ -146,7 +160,11 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af } } else { this.modelValue = null; - this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); + if (this.localBrowserTimezonePlaceholderOnEmptyValue) { + this.selectTimezoneFormGroup.get('timezone').patchValue(this.getLocalBrowserTimezoneInfoPlaceholder(), {emitEvent: false}); + } else { + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); + } } this.dirty = true; } @@ -162,11 +180,17 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af if (this.ignoreClosePanel) { this.ignoreClosePanel = false; } else { - if (!this.modelValue && (this.defaultTimezoneId || this.userTimezoneByDefaultValue)) { - const defaultTimezoneInfo = getTimezoneInfo(this.defaultTimezoneId, this.defaultTimezoneId, this.userTimezoneByDefaultValue); - if (defaultTimezoneInfo !== null) { + if (!this.modelValue) { + if (this.defaultTimezoneId || this.userTimezoneByDefaultValue) { + const defaultTimezoneInfo = getTimezoneInfo(this.defaultTimezoneId, this.defaultTimezoneId, this.userTimezoneByDefaultValue); + if (defaultTimezoneInfo !== null) { + this.ngZone.run(() => { + this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); + }); + } + } else if (this.localBrowserTimezonePlaceholderOnEmptyValue) { this.ngZone.run(() => { - this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); + this.selectTimezoneFormGroup.get('timezone').reset(this.getLocalBrowserTimezoneInfoPlaceholder(), {emitEvent: true}); }); } } @@ -187,10 +211,10 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af fetchTimezones(searchText?: string): Observable> { this.searchText = searchText; if (searchText && searchText.length) { - return of(getTimezones().filter((timezoneInfo) => + return of(this.loadTimezones().filter((timezoneInfo) => timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase()))); } - return of(getTimezones()); + return of(this.loadTimezones()); } clear() { @@ -200,4 +224,23 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af }, 0); } + private loadTimezones(): Array { + if (!this.timezones) { + this.timezones = []; + if (this.localBrowserTimezonePlaceholderOnEmptyValue) { + this.timezones.push(this.getLocalBrowserTimezoneInfoPlaceholder()); + } + this.timezones.push(...getTimezones()); + } + return this.timezones; + } + + private getLocalBrowserTimezoneInfoPlaceholder(): TimezoneInfo { + if (!this.localBrowserTimezoneInfoPlaceholder) { + this.localBrowserTimezoneInfoPlaceholder = deepClone(getDefaultTimezoneInfo()); + this.localBrowserTimezoneInfoPlaceholder.id = null; + this.localBrowserTimezoneInfoPlaceholder.name = this.translate.instant('timezone.browser-time'); + } + return this.localBrowserTimezoneInfoPlaceholder; + } } diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index b0dd744072..3251991440 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -30,6 +30,9 @@ import { TsValue } from '@shared/models/query/query.models'; import { PageData } from '@shared/models/page/page-data'; +import { alarmFields } from '@shared/models/alarm.models'; +import { entityFields } from '@shared/models/entity.models'; +import { isUndefined } from '@core/utils'; export enum DataKeyType { timeseries = 'timeseries', @@ -446,7 +449,9 @@ export class EntityDataUpdate extends DataUpdate { for (const key of Object.keys(entityData.timeseries)) { const tsValues = entityData.timeseries[key]; for (const tsValue of tsValues) { - tsValue.ts += tsOffset; + if (tsValue.ts) { + tsValue.ts += tsOffset; + } } } } @@ -455,13 +460,17 @@ export class EntityDataUpdate extends DataUpdate { const keyTypeValues = entityData.latest[entityKeyType]; for (const key of Object.keys(keyTypeValues)) { const tsValue = keyTypeValues[key]; - tsValue.ts += tsOffset; + if (tsValue.ts) { + tsValue.ts += tsOffset; + } + if (key === entityFields.createdTime.keyName && tsValue.value) { + tsValue.value = (Number(tsValue.value) + tsOffset) + ''; + } } } } } } - } export class AlarmDataUpdate extends DataUpdate { @@ -473,6 +482,48 @@ export class AlarmDataUpdate extends DataUpdate { this.allowedEntities = msg.allowedEntities; this.totalEntities = msg.totalEntities; } + + public prepareData(tsOffset: number) { + if (this.data) { + this.processAlarmData(this.data.data, tsOffset); + } + if (this.update) { + this.processAlarmData(this.update, tsOffset); + } + } + + private processAlarmData(data: Array, tsOffset: number) { + for (const alarmData of data) { + alarmData.createdTime += tsOffset; + if (alarmData.ackTs) { + alarmData.ackTs += tsOffset; + } + if (alarmData.clearTs) { + alarmData.clearTs += tsOffset; + } + if (alarmData.endTs) { + alarmData.endTs += tsOffset; + } + if (alarmData.latest) { + for (const entityKeyType of Object.keys(alarmData.latest)) { + const keyTypeValues = alarmData.latest[entityKeyType]; + for (const key of Object.keys(keyTypeValues)) { + const tsValue = keyTypeValues[key]; + if (tsValue.ts) { + tsValue.ts += tsOffset; + } + if (key in [entityFields.createdTime.keyName, + alarmFields.startTime.keyName, + alarmFields.endTime.keyName, + alarmFields.ackTime.keyName, + alarmFields.clearTime.keyName] && tsValue.value) { + tsValue.value = (Number(tsValue.value) + tsOffset) + ''; + } + } + } + } + } + } } export class EntityCountUpdate extends CmdUpdate { @@ -500,7 +551,7 @@ export class TelemetrySubscriber { private zone: NgZone; - private tsOffset = 0; + private tsOffset = undefined; public subscriptionCommands: Array; @@ -556,8 +607,14 @@ export class TelemetrySubscriber { this.reconnectSubject.complete(); } - public setTsOffset(tsOffset: number) { - this.tsOffset = tsOffset; + public setTsOffset(tsOffset: number): boolean { + if (this.tsOffset !== tsOffset) { + const changed = !isUndefined(this.tsOffset); + this.tsOffset = tsOffset; + return changed; + } else { + return false; + } } public onData(message: SubscriptionUpdate) { @@ -598,6 +655,9 @@ export class TelemetrySubscriber { } public onAlarmData(message: AlarmDataUpdate) { + if (this.tsOffset) { + message.prepareData(this.tsOffset); + } if (this.zone) { this.zone.run( () => { 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 b4a83992ed..0c6a2c4ac6 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -318,6 +318,16 @@ export function toHistoryTimewindow(timewindow: Timewindow, startTimeMs: number, return historyTimewindow; } +export function calculateTsOffset(timezone?: string): number { + if (timezone) { + const tz = getTimezone(timezone); + const localOffset = moment().utcOffset(); + return (tz.utcOffset() - localOffset) * 60 * 1000; + } else { + return 0; + } +} + export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: number, stateData: boolean, timeService: TimeService): SubscriptionTimewindow { const subscriptionTimewindow: SubscriptionTimewindow = { @@ -329,13 +339,8 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num type: AggregationType.AVG }, timezone: timewindow.timezone, - tsOffset: 0 + tsOffset: calculateTsOffset(timewindow.timezone) }; - if (timewindow.timezone) { - const tz = getTimezone(timewindow.timezone); - const localOffset = moment().utcOffset(); - subscriptionTimewindow.tsOffset = (tz.utcOffset() - localOffset) * 60 * 1000; - } let aggTimewindow = 0; if (stateData) { subscriptionTimewindow.aggregation.type = AggregationType.NONE; @@ -407,6 +412,7 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num endTimeMs: calculateIntervalEndTime(timewindow.history.quickInterval, currentDate) }; aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; + subscriptionTimewindow.quickInterval = timewindow.history.quickInterval; } else { subscriptionTimewindow.fixedWindow = { startTimeMs: timewindow.history.fixedTimewindow.startTimeMs - subscriptionTimewindow.tsOffset, @@ -768,6 +774,11 @@ export function getTimezoneInfo(timezoneId: string, defaultTimezoneId?: string, return foundTimezone; } +export function getDefaultTimezoneInfo(): TimezoneInfo { + const userTimezone = getDefaultTimezone(); + return getTimezoneInfo(userTimezone); +} + export function getDefaultTimezone(): string { if (!defaultTimezone) { defaultTimezone = monentTz.tz.guess(); 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 b2922f3609..8b4b6b14af 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1988,7 +1988,8 @@ "timezone": "Timezone", "select-timezone": "Select timezone", "no-timezones-matching": "No timezones matching '{{timezone}}' were found.", - "timezone-required": "Timezone is required." + "timezone-required": "Timezone is required.", + "browser-time": "Browser Time" }, "queue": { "select_name": "Select queue name",