Commit 10cea37abe9249459a4e43e6a4e42edcba06f774

Authored by Vladyslav_Prykhodko
Committed by Vladyslav Prykhodko
1 parent d520415d

UI: Add new setting for subscription reloadOnlyOnDataUpdated

... ... @@ -47,7 +47,7 @@
47 47 "resources": [],
48 48 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>",
49 49 "templateCss": "",
50   - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}",
  50 + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n reloadOnlyOnDataUpdated: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}",
51 51 "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}",
52 52 "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
53 53 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}"
... ... @@ -134,4 +134,4 @@
134 134 }
135 135 }
136 136 ]
137   -}
\ No newline at end of file
  137 +}
... ...
... ... @@ -71,6 +71,7 @@ export class DataAggregator {
71 71
72 72 private dataReceived = false;
73 73 private resetPending = false;
  74 + private updatedData = false;
74 75
75 76 private noAggregation = this.aggregationType === AggregationType.NONE;
76 77 private aggregationTimeout = Math.max(this.interval, 1000);
... ... @@ -90,7 +91,8 @@ export class DataAggregator {
90 91 private timeWindow: number,
91 92 private interval: number,
92 93 private stateData: boolean,
93   - private utils: UtilsService) {
  94 + private utils: UtilsService,
  95 + private isReloadOnlyOnDataUpdated: boolean) {
94 96 this.tsKeyNames.forEach((key) => {
95 97 this.dataBuffer[key] = [];
96 98 });
... ... @@ -140,6 +142,7 @@ export class DataAggregator {
140 142 this.elapsed = 0;
141 143 this.aggregationTimeout = Math.max(this.interval, 1000);
142 144 this.resetPending = true;
  145 + this.updatedData = false;
143 146 this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), this.aggregationTimeout);
144 147 }
145 148
... ... @@ -180,6 +183,7 @@ export class DataAggregator {
180 183 this.onInterval(history, detectChanges);
181 184 }
182 185 }
  186 + this.updatedData = true;
183 187 }
184 188
185 189 private onInterval(history?: boolean, detectChanges?: boolean) {
... ... @@ -201,8 +205,9 @@ export class DataAggregator {
201 205 } else {
202 206 this.data = this.updateData();
203 207 }
204   - if (this.onDataCb) {
  208 + if (this.onDataCb && (!this.isReloadOnlyOnDataUpdated || this.updatedData)) {
205 209 this.onDataCb(this.data, detectChanges);
  210 + this.updatedData = false;
206 211 }
207 212 if (!history) {
208 213 this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), this.aggregationTimeout);
... ... @@ -223,6 +228,7 @@ export class DataAggregator {
223 228 this.lastPrevKvPairData[key] = [aggTimestamp, aggData.aggValue];
224 229 }
225 230 aggKeyData.delete(aggTimestamp);
  231 + this.updatedData = true;
226 232 } else if (aggTimestamp <= this.endTs) {
227 233 const kvPair: [number, any] = [aggTimestamp, aggData.aggValue];
228 234 keyData.push(kvPair);
... ...
... ... @@ -66,6 +66,7 @@ export interface EntityDataSubscriptionOptions {
66 66 type: widgetType;
67 67 entityFilter?: EntityFilter;
68 68 isPaginatedDataSubscription?: boolean;
  69 + isReloadOnlyOnDataUpdated?: boolean;
69 70 pageLink?: EntityDataPageLink;
70 71 keyFilters?: Array<KeyFilter>;
71 72 additionalKeyFilters?: Array<KeyFilter>;
... ... @@ -671,7 +672,8 @@ export class EntityDataSubscription {
671 672 subsTw.aggregation.timeWindow,
672 673 subsTw.aggregation.interval,
673 674 subsTw.aggregation.stateData,
674   - this.utils
  675 + this.utils,
  676 + this.entityDataSubscriptionOptions.isReloadOnlyOnDataUpdated
675 677 );
676 678 }
677 679
... ...
... ... @@ -60,7 +60,8 @@ export class EntityDataService {
60 60 constructor(private telemetryService: TelemetryWebsocketService,
61 61 private utils: UtilsService) {}
62 62
63   - public prepareSubscription(listener: EntityDataListener): Observable<EntityDataLoadResult> {
  63 + public prepareSubscription(listener: EntityDataListener,
  64 + isReloadOnlyOnDataUpdated = false): Observable<EntityDataLoadResult> {
64 65 const datasource = listener.configDatasource;
65 66 listener.subscriptionOptions = this.createSubscriptionOptions(
66 67 datasource,
... ... @@ -68,7 +69,8 @@ export class EntityDataService {
68 69 datasource.pageLink,
69 70 datasource.keyFilters,
70 71 null,
71   - false);
  72 + false,
  73 + isReloadOnlyOnDataUpdated);
72 74 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !datasource.pageLink)) {
73 75 return of(null);
74 76 }
... ... @@ -87,7 +89,8 @@ export class EntityDataService {
87 89
88 90 public subscribeForPaginatedData(listener: EntityDataListener,
89 91 pageLink: EntityDataPageLink,
90   - keyFilters: KeyFilter[]): Observable<EntityDataLoadResult> {
  92 + keyFilters: KeyFilter[],
  93 + isReloadOnlyOnDataUpdated = false): Observable<EntityDataLoadResult> {
91 94 const datasource = listener.configDatasource;
92 95 listener.subscriptionOptions = this.createSubscriptionOptions(
93 96 datasource,
... ... @@ -95,7 +98,8 @@ export class EntityDataService {
95 98 pageLink,
96 99 datasource.keyFilters,
97 100 keyFilters,
98   - true);
  101 + true,
  102 + isReloadOnlyOnDataUpdated);
99 103 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) {
100 104 listener.dataLoaded(emptyPageData<EntityData>(), [],
101 105 listener.configDatasourceIndex, listener.subscriptionOptions.pageLink);
... ... @@ -119,7 +123,8 @@ export class EntityDataService {
119 123 pageLink: EntityDataPageLink,
120 124 keyFilters: KeyFilter[],
121 125 additionalKeyFilters: KeyFilter[],
122   - isPaginatedDataSubscription: boolean): EntityDataSubscriptionOptions {
  126 + isPaginatedDataSubscription: boolean,
  127 + isReloadOnlyOnDataUpdated: boolean): EntityDataSubscriptionOptions {
123 128 const subscriptionDataKeys: Array<SubscriptionDataKey> = [];
124 129 datasource.dataKeys.forEach((dataKey) => {
125 130 const subscriptionDataKey: SubscriptionDataKey = {
... ... @@ -142,6 +147,7 @@ export class EntityDataService {
142 147 entityDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters;
143 148 }
144 149 entityDataSubscriptionOptions.isPaginatedDataSubscription = isPaginatedDataSubscription;
  150 + entityDataSubscriptionOptions.isReloadOnlyOnDataUpdated = isReloadOnlyOnDataUpdated;
145 151 return entityDataSubscriptionOptions;
146 152 }
147 153 }
... ...
... ... @@ -226,6 +226,7 @@ export interface WidgetSubscriptionOptions {
226 226 hasDataPageLink?: boolean;
227 227 singleEntity?: boolean;
228 228 warnOnPageDataOverflow?: boolean;
  229 + reloadOnlyOnDataUpdated?: boolean;
229 230 targetDeviceAliasIds?: Array<string>;
230 231 targetDeviceIds?: Array<string>;
231 232 useDashboardTimewindow?: boolean;
... ...
... ... @@ -83,6 +83,7 @@ export class WidgetSubscription implements IWidgetSubscription {
83 83 hasDataPageLink: boolean;
84 84 singleEntity: boolean;
85 85 warnOnPageDataOverflow: boolean;
  86 + reloadOnlyOnDataUpdated: boolean;
86 87
87 88 datasourcePages: PageData<Datasource>[];
88 89 dataPages: PageData<Array<DatasourceData>>[];
... ... @@ -200,6 +201,7 @@ export class WidgetSubscription implements IWidgetSubscription {
200 201 this.hasDataPageLink = options.hasDataPageLink;
201 202 this.singleEntity = options.singleEntity;
202 203 this.warnOnPageDataOverflow = options.warnOnPageDataOverflow;
  204 + this.reloadOnlyOnDataUpdated = options.reloadOnlyOnDataUpdated;
203 205 this.datasourcePages = [];
204 206 this.datasources = [];
205 207 this.dataPages = [];
... ... @@ -423,7 +425,7 @@ export class WidgetSubscription implements IWidgetSubscription {
423 425 }
424 426 };
425 427 this.entityDataListeners.push(listener);
426   - return this.ctx.entityDataService.prepareSubscription(listener);
  428 + return this.ctx.entityDataService.prepareSubscription(listener, this.reloadOnlyOnDataUpdated);
427 429 });
428 430 return forkJoin(resolveResultObservables).pipe(
429 431 map((resolveResults) => {
... ... @@ -815,7 +817,7 @@ export class WidgetSubscription implements IWidgetSubscription {
815 817 }
816 818 };
817 819 this.entityDataListeners[datasourceIndex] = entityDataListener;
818   - return this.ctx.entityDataService.subscribeForPaginatedData(entityDataListener, pageLink, keyFilters);
  820 + return this.ctx.entityDataService.subscribeForPaginatedData(entityDataListener, pageLink, keyFilters, this.reloadOnlyOnDataUpdated);
819 821 } else {
820 822 return of(null);
821 823 }
... ...
... ... @@ -40,7 +40,7 @@ import {
40 40 } from '@shared/models/widget.models';
41 41 import { UtilsService } from '@core/services/utils.service';
42 42 import { TranslateService } from '@ngx-translate/core';
43   -import { hashCode, isDefined, isEqual, isNumber } from '@core/utils';
  43 +import { hashCode, isDefined, isNumber } from '@core/utils';
44 44 import cssjs from '@core/css/css';
45 45 import { PageLink } from '@shared/models/page/page-link';
46 46 import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order';
... ... @@ -578,8 +578,7 @@ class TimeseriesDatasource implements DataSource<TimeseriesRow> {
578 578
579 579 private fetchRows(pageLink: PageLink): Observable<PageData<TimeseriesRow>> {
580 580 return this.allRows$.pipe(
581   - map((data) => pageLink.filterData(data)),
582   - distinctUntilChanged((prev, curr) => isEqual(prev, curr))
  581 + map((data) => pageLink.filterData(data))
583 582 );
584 583 }
585 584 }
... ...
... ... @@ -485,6 +485,9 @@ export class WidgetComponentService {
485 485 if (isUndefined(result.typeParameters.warnOnPageDataOverflow)) {
486 486 result.typeParameters.warnOnPageDataOverflow = true;
487 487 }
  488 + if (isUndefined(result.typeParameters.reloadOnlyOnDataUpdated)) {
  489 + result.typeParameters.reloadOnlyOnDataUpdated = false;
  490 + }
488 491 if (isUndefined(result.typeParameters.dataKeysOptional)) {
489 492 result.typeParameters.dataKeysOptional = false;
490 493 }
... ...
... ... @@ -895,6 +895,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
895 895 hasDataPageLink: this.typeParameters.hasDataPageLink,
896 896 singleEntity: this.typeParameters.singleEntity,
897 897 warnOnPageDataOverflow: this.typeParameters.warnOnPageDataOverflow,
  898 + reloadOnlyOnDataUpdated: this.typeParameters.reloadOnlyOnDataUpdated,
898 899 comparisonEnabled: comparisonSettings.comparisonEnabled,
899 900 timeForComparison: comparisonSettings.timeForComparison
900 901 };
... ...
... ... @@ -154,6 +154,7 @@ export interface WidgetTypeParameters {
154 154 hasDataPageLink?: boolean;
155 155 singleEntity?: boolean;
156 156 warnOnPageDataOverflow?: boolean;
  157 + reloadOnlyOnDataUpdated?: boolean;
157 158 }
158 159
159 160 export interface WidgetControllerDescriptor {
... ...