From 391c88e8cba1183de1d2990b524c9fcfce53736c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 24 Jun 2020 11:31:52 +0300 Subject: [PATCH] UI: Improve data subscription reconnect flow --- .../app/core/api/entity-data-subscription.ts | 50 ++++++++++++++----- .../src/app/core/api/entity-data.service.ts | 11 ++-- ui-ngx/src/app/core/api/widget-api.models.ts | 9 ++-- .../src/app/core/api/widget-subscription.ts | 14 ++++-- .../lib/entities-table-widget.component.ts | 2 +- .../components/widget/widget.component.ts | 3 ++ 6 files changed, 62 insertions(+), 27 deletions(-) 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 5db1827236..1a37c0a7b7 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -37,7 +37,7 @@ import { } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service'; -import { deepClone, isDefinedAndNotNull, isObject, objectHashCode } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isEqual, isObject, objectHashCode } from '@core/utils'; import { PageData } from '@shared/models/page/page-data'; import { DataAggregator } from '@core/api/data-aggregator'; import { NULL_UUID } from '@shared/models/id/has-uuid'; @@ -50,7 +50,7 @@ export interface EntityDataSubscriptionOptions { dataKeys: Array; type: widgetType; entityFilter?: EntityFilter; - isLatestDataSubscription?: boolean; + isPaginatedDataSubscription?: boolean; pageLink?: EntityDataPageLink; keyFilters?: Array; subscriptionTimewindow?: SubscriptionTimewindow; @@ -154,7 +154,7 @@ export class EntityDataSubscription { } public subscribe(): Observable { - if (!this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (!this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { this.entityDataResolveSubject = new ReplaySubject(1); } else { this.started = true; @@ -199,7 +199,7 @@ export class EntityDataSubscription { latestValues: this.latestValues }; - if (this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.entityDataSubscriptionOptions.type === widgetType.latest) { if (this.latestValues.length > 0) { this.dataCommand.latestCmd = { @@ -222,7 +222,7 @@ export class EntityDataSubscription { ); this.subscriber.reconnect$.subscribe(() => { - if (this.started && !this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (this.started && !this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && !this.history && this.tsFields.length) { const newSubsTw: SubscriptionTimewindow = this.listener.updateRealtimeSubscription(); @@ -271,14 +271,14 @@ export class EntityDataSubscription { totalPages: 1 }; this.onPageData(pageData); - if (this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.entityDataSubscriptionOptions.type === widgetType.latest) { this.frequency = 1000; this.timer = setTimeout(this.onTick.bind(this, true), 0); } } } - if (this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { return of(null); } else { return this.entityDataResolveSubject.asObservable(); @@ -286,7 +286,7 @@ export class EntityDataSubscription { } public start() { - if (this.entityDataSubscriptionOptions.isLatestDataSubscription) { + if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { return; } this.subsTw = this.entityDataSubscriptionOptions.subscriptionTimewindow; @@ -432,9 +432,26 @@ export class EntityDataSubscription { } } + private pageDataChanged(prevPageData: PageData, nextPageData: PageData) { + const prevIds = prevPageData.data.map((entityData) => entityData.entityId.id); + const nextIds = nextPageData.data.map((entityData) => entityData.entityId.id); + return !isEqual(prevIds, nextIds); + } + private onPageData(pageData: PageData) { + const isInitialData = !this.pageData; + if (!isInitialData && !this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { + if (this.pageDataChanged(this.pageData, pageData)) { + if (this.listener.initialPageDataChanged) { + this.listener.initialPageDataChanged(pageData); + } + return; + } + } this.pageData = pageData; - this.resetData(); + if (isInitialData || this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { + this.resetData(); + } const data: Array> = []; for (let dataIndex = 0; dataIndex < pageData.data.length; dataIndex++) { const entityData = pageData.data[dataIndex]; @@ -458,8 +475,10 @@ export class EntityDataSubscription { ); this.entityDataResolveSubject.complete(); } else { - this.listener.dataLoaded(pageData, data, - this.listener.configDatasourceIndex); + if (isInitialData || this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { + this.listener.dataLoaded(pageData, data, + this.listener.configDatasourceIndex); + } } } @@ -487,8 +506,13 @@ export class EntityDataSubscription { } if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && entityData.timeseries) { const subscriptionData = this.toSubscriptionData(entityData.timeseries, true); - if (!this.history && aggregate) { - this.dataAggregators[dataIndex].onData({data: subscriptionData}, false, false, true); + if (!this.history) { + if (this.dataAggregators && this.dataAggregators[dataIndex]) { + this.dataAggregators[dataIndex].onData({data: subscriptionData}, false, false, true); + } + if (!aggregate) { + this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); + } } else { this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); } 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 cddc9bcba0..992e842227 100644 --- a/ui-ngx/src/app/core/api/entity-data.service.ts +++ b/ui-ngx/src/app/core/api/entity-data.service.ts @@ -33,6 +33,7 @@ export interface EntityDataListener { configDatasourceIndex: number; dataLoaded: (pageData: PageData, data: Array>, datasourceIndex: number) => void; dataUpdated: (data: DataSetHolder, datasourceIndex: number, dataIndex: number, dataKeyIndex: number, detectChanges: boolean) => void; + initialPageDataChanged?: (nextPageData: PageData) => void; updateRealtimeSubscription?: () => SubscriptionTimewindow; setRealtimeSubscription?: (subscriptionTimewindow: SubscriptionTimewindow) => void; subscription?: EntityDataSubscription; @@ -70,9 +71,9 @@ export class EntityDataService { listener.subscription.start(); } - public subscribeForLatestData(listener: EntityDataListener, - pageLink: EntityDataPageLink, - keyFilters: KeyFilter[]) { + public subscribeForPaginatedData(listener: EntityDataListener, + pageLink: EntityDataPageLink, + keyFilters: KeyFilter[]) { const datasource = listener.configDatasource; if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) { return; @@ -89,7 +90,7 @@ export class EntityDataService { private createSubscription(listener: EntityDataListener, pageLink: EntityDataPageLink, keyFilters: KeyFilter[], - isLatestDataSubscription: boolean): EntityDataSubscription { + isPaginatedDataSubscription: boolean): EntityDataSubscription { const datasource = listener.configDatasource; const subscriptionDataKeys: Array = []; datasource.dataKeys.forEach((dataKey) => { @@ -111,7 +112,7 @@ export class EntityDataService { entityDataSubscriptionOptions.pageLink = pageLink; entityDataSubscriptionOptions.keyFilters = keyFilters; } - entityDataSubscriptionOptions.isLatestDataSubscription = isLatestDataSubscription; + entityDataSubscriptionOptions.isPaginatedDataSubscription = isPaginatedDataSubscription; return new EntityDataSubscription(entityDataSubscriptionOptions, listener, this.telemetryService, this.utils); } diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index c6a39e726b..53d5b2d280 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -41,7 +41,7 @@ import { EntityAliases } from '@shared/models/alias.models'; import { EntityInfo } from '@app/shared/models/entity.models'; import { IDashboardComponent } from '@home/models/dashboard-component.models'; import * as moment_ from 'moment'; -import { EntityDataPageLink, EntityFilter, KeyFilter } from '@shared/models/query/query.models'; +import { EntityData, EntityDataPageLink, EntityFilter, KeyFilter } from '@shared/models/query/query.models'; import { EntityDataService } from '@core/api/entity-data.service'; import { PageData } from '@shared/models/page/page-data'; @@ -185,6 +185,7 @@ export class WidgetSubscriptionContext { export interface WidgetSubscriptionCallbacks { onDataUpdated?: (subscription: IWidgetSubscription, detectChanges: boolean) => void; onDataUpdateError?: (subscription: IWidgetSubscription, e: any) => void; + onInitialPageDataChanged?: (subscription: IWidgetSubscription, nextPageData: PageData) => void; dataLoading?: (subscription: IWidgetSubscription) => void; legendDataUpdated?: (subscription: IWidgetSubscription, detectChanges: boolean) => void; timeWindowUpdated?: (subscription: IWidgetSubscription, timeWindowConfig: Timewindow) => void; @@ -279,9 +280,9 @@ export interface IWidgetSubscription { subscribe(): void; - subscribeForLatestData(datasourceIndex: number, - pageLink: EntityDataPageLink, - keyFilters: KeyFilter[]): void; + subscribeForPaginatedData(datasourceIndex: number, + pageLink: EntityDataPageLink, + keyFilters: KeyFilter[]): void; isDataResolved(): boolean; diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 8c1e98411b..7e7f0ca75d 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -209,6 +209,7 @@ export class WidgetSubscription implements IWidgetSubscription { } else { this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || (() => {}); this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || (() => {}); + this.callbacks.onInitialPageDataChanged = this.callbacks.onInitialPageDataChanged || (() => {}); this.callbacks.dataLoading = this.callbacks.dataLoading || (() => {}); this.callbacks.legendDataUpdated = this.callbacks.legendDataUpdated || (() => {}); this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || (() => {}); @@ -400,6 +401,7 @@ export class WidgetSubscription implements IWidgetSubscription { dataLoaded: (pageData, data1, datasourceIndex) => { this.dataLoaded(pageData, data1, datasourceIndex, true) }, + initialPageDataChanged: this.initialPageDataChanged.bind(this), dataUpdated: this.dataUpdated.bind(this), updateRealtimeSubscription: () => { this.subscriptionTimewindow = this.updateRealtimeSubscription(); @@ -852,9 +854,9 @@ export class WidgetSubscription implements IWidgetSubscription { } } - subscribeForLatestData(datasourceIndex: number, - pageLink: EntityDataPageLink, - keyFilters: KeyFilter[]): void { + subscribeForPaginatedData(datasourceIndex: number, + pageLink: EntityDataPageLink, + keyFilters: KeyFilter[]): void { let entityDataListener = this.entityDataListeners[datasourceIndex]; if (entityDataListener) { this.ctx.entityDataService.stopSubscription(entityDataListener); @@ -871,7 +873,7 @@ export class WidgetSubscription implements IWidgetSubscription { dataUpdated: this.dataUpdated.bind(this) }; this.entityDataListeners[datasourceIndex] = entityDataListener; - this.ctx.entityDataService.subscribeForLatestData(entityDataListener, pageLink, keyFilters); + this.ctx.entityDataService.subscribeForPaginatedData(entityDataListener, pageLink, keyFilters); } } @@ -1156,6 +1158,10 @@ export class WidgetSubscription implements IWidgetSubscription { return this.timewindowForComparison; } + private initialPageDataChanged(nextPageData: PageData) { + this.callbacks.onInitialPageDataChanged(this, nextPageData); + } + private dataLoaded(pageData: PageData, data: Array>, datasourceIndex: number, isUpdate: boolean) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index ab248733b6..d24ed9cc37 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -614,7 +614,7 @@ class EntityDatasource implements DataSource { } loadEntities(pageLink: EntityDataPageLink, keyFilters: KeyFilter[]) { - this.subscription.subscribeForLatestData(0, pageLink, keyFilters); + this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); /* this.fetchEntities(pageLink).pipe( catchError(() => of(emptyPageData())), ).subscribe( diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index 64419bb996..237beb6397 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -818,6 +818,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI onDataUpdateError: (subscription, e) => { this.handleWidgetException(e); }, + onInitialPageDataChanged: (subscription, nextPageData) => { + this.reInit(); + }, dataLoading: (subscription) => { if (this.loadingData !== subscription.loadingData) { this.loadingData = subscription.loadingData;