Browse Source

Merge branch 'feature/entity-data-query' of github.com:thingsboard/thingsboard into feature/entity-data-query

pull/3053/head
Andrii Shvaika 6 years ago
parent
commit
f4137bc9ea
  1. 16
      application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
  2. 41
      ui-ngx/src/app/core/api/entity-data-subscription.ts
  3. 2
      ui-ngx/src/app/core/api/widget-api.models.ts
  4. 128
      ui-ngx/src/app/core/api/widget-subscription.ts
  5. 4
      ui-ngx/src/app/core/services/utils.service.ts
  6. 3
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  7. 3
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  8. 1
      ui-ngx/src/app/shared/models/query/query.models.ts
  9. 2
      ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts
  10. 3
      ui-ngx/src/app/shared/models/widget.models.ts

16
application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json

File diff suppressed because one or more lines are too long

41
ui-ngx/src/app/core/api/entity-data-subscription.ts

@ -232,13 +232,7 @@ export class EntityDataSubscription {
this.subsCommand.tsCmd.interval = this.subsTw.aggregation.interval; this.subsCommand.tsCmd.interval = this.subsTw.aggregation.interval;
this.subsCommand.tsCmd.limit = this.subsTw.aggregation.limit; this.subsCommand.tsCmd.limit = this.subsTw.aggregation.limit;
this.subsCommand.tsCmd.agg = this.subsTw.aggregation.type; this.subsCommand.tsCmd.agg = this.subsTw.aggregation.type;
if (this.subsTw.aggregation.stateData) { this.subsCommand.tsCmd.fetchLatestPreviousPoint = this.subsTw.aggregation.stateData;
this.subsCommand.historyCmd.startTs = this.subsTw.startTs - YEAR;
this.subsCommand.historyCmd.endTs = this.subsTw.startTs;
this.subsCommand.historyCmd.interval = this.subsTw.aggregation.interval;
this.subsCommand.historyCmd.limit = this.subsTw.aggregation.limit;
this.subsCommand.historyCmd.agg = this.subsTw.aggregation.type;
}
this.dataAggregators.forEach((dataAggregator) => { this.dataAggregators.forEach((dataAggregator) => {
dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval);
}); });
@ -324,7 +318,7 @@ export class EntityDataSubscription {
if (this.datasourceType === DatasourceType.function) { if (this.datasourceType === DatasourceType.function) {
this.dataAggregators[dataIndex] = this.createRealtimeDataAggregator(this.subsTw, tsKeyNames, this.dataAggregators[dataIndex] = this.createRealtimeDataAggregator(this.subsTw, tsKeyNames,
DataKeyType.function, dataIndex, this.notifyListener.bind(this)); DataKeyType.function, dataIndex, this.notifyListener.bind(this));
} else if (!this.history && tsKeyNames.length) { } else if (tsKeyNames.length) {
this.dataAggregators[dataIndex] = this.createRealtimeDataAggregator(this.subsTw, tsKeyNames, this.dataAggregators[dataIndex] = this.createRealtimeDataAggregator(this.subsTw, tsKeyNames,
DataKeyType.timeseries, dataIndex, this.notifyListener.bind(this)); DataKeyType.timeseries, dataIndex, this.notifyListener.bind(this));
} }
@ -342,11 +336,9 @@ export class EntityDataSubscription {
endTs: this.subsTw.fixedWindow.endTimeMs, endTs: this.subsTw.fixedWindow.endTimeMs,
interval: this.subsTw.aggregation.interval, interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit, limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type agg: this.subsTw.aggregation.type,
fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
}; };
if (this.subsTw.aggregation.stateData) {
this.subsCommand.historyCmd.startTs -= YEAR;
}
} else { } else {
this.subsCommand.tsCmd = { this.subsCommand.tsCmd = {
keys: this.tsFields.map(key => key.key), keys: this.tsFields.map(key => key.key),
@ -354,17 +346,8 @@ export class EntityDataSubscription {
timeWindow: this.subsTw.aggregation.timeWindow, timeWindow: this.subsTw.aggregation.timeWindow,
interval: this.subsTw.aggregation.interval, interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit, limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type agg: this.subsTw.aggregation.type,
} fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
if (this.subsTw.aggregation.stateData) {
this.subsCommand.historyCmd = {
keys: this.tsFields.map(key => key.key),
startTs: this.subsTw.startTs - YEAR,
endTs: this.subsTw.startTs,
interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type
};
} }
} }
} }
@ -506,14 +489,10 @@ export class EntityDataSubscription {
} }
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && entityData.timeseries) { if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && entityData.timeseries) {
const subscriptionData = this.toSubscriptionData(entityData.timeseries, true); const subscriptionData = this.toSubscriptionData(entityData.timeseries, true);
if (!this.history) { if (this.dataAggregators && this.dataAggregators[dataIndex]) {
if (this.dataAggregators && this.dataAggregators[dataIndex]) { this.dataAggregators[dataIndex].onData({data: subscriptionData}, false, this.history, true);
this.dataAggregators[dataIndex].onData({data: subscriptionData}, false, false, true); }
} if (!this.history && !aggregate) {
if (!aggregate) {
this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb);
}
} else {
this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb);
} }
} }

2
ui-ngx/src/app/core/api/widget-api.models.ts

@ -44,6 +44,7 @@ import * as moment_ from 'moment';
import { EntityData, 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 { EntityDataService } from '@core/api/entity-data.service';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { TranslateService } from '@ngx-translate/core';
export interface TimewindowFunctions { export interface TimewindowFunctions {
onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void;
@ -174,6 +175,7 @@ export class WidgetSubscriptionContext {
timeService: TimeService; timeService: TimeService;
deviceService: DeviceService; deviceService: DeviceService;
alarmService: AlarmService; alarmService: AlarmService;
translate: TranslateService;
// datasourceService: DatasourceService; // datasourceService: DatasourceService;
entityDataService: EntityDataService; entityDataService: EntityDataService;
utils: UtilsService; utils: UtilsService;

128
ui-ngx/src/app/core/api/widget-subscription.ts

@ -22,6 +22,7 @@ import {
WidgetSubscriptionOptions WidgetSubscriptionOptions
} from '@core/api/widget-api.models'; } from '@core/api/widget-api.models';
import { import {
DataKey,
DataSet, DataSet,
DataSetHolder, DataSetHolder,
Datasource, Datasource,
@ -393,6 +394,28 @@ export class WidgetSubscription implements IWidgetSubscription {
this.notifyDataLoaded(); this.notifyDataLoaded();
return of(null); return of(null);
} }
if (this.comparisonEnabled) {
const additionalDatasources: Datasource[] = [];
this.configuredDatasources.forEach((datasource, datasourceIndex) => {
const additionalDataKeys: DataKey[] = [];
datasource.dataKeys.forEach((dataKey, dataKeyIndex) => {
if (dataKey.settings.comparisonSettings && dataKey.settings.comparisonSettings.showValuesForComparison) {
const additionalDataKey = deepClone(dataKey);
additionalDataKey.isAdditional = true;
additionalDataKey.origDataKeyIndex = dataKeyIndex;
additionalDataKeys.push(additionalDataKey);
}
});
if (additionalDataKeys.length) {
const additionalDatasource: Datasource = deepClone(datasource);
additionalDatasource.dataKeys = additionalDataKeys;
additionalDatasource.isAdditional = true;
additionalDatasource.origDatasourceIndex = datasourceIndex;
additionalDatasources.push(additionalDatasource);
}
});
this.configuredDatasources = this.configuredDatasources.concat(additionalDatasources);
}
const resolveResultObservables = this.configuredDatasources.map((datasource, index) => { const resolveResultObservables = this.configuredDatasources.map((datasource, index) => {
const listener: EntityDataListener = { const listener: EntityDataListener = {
subscriptionType: this.type, subscriptionType: this.type,
@ -404,11 +427,18 @@ export class WidgetSubscription implements IWidgetSubscription {
initialPageDataChanged: this.initialPageDataChanged.bind(this), initialPageDataChanged: this.initialPageDataChanged.bind(this),
dataUpdated: this.dataUpdated.bind(this), dataUpdated: this.dataUpdated.bind(this),
updateRealtimeSubscription: () => { updateRealtimeSubscription: () => {
this.subscriptionTimewindow = this.updateRealtimeSubscription(); if (this.comparisonEnabled && datasource.isAdditional) {
return this.subscriptionTimewindow; return this.updateSubscriptionForComparison();
} else {
return this.updateRealtimeSubscription();
}
}, },
setRealtimeSubscription: (subscriptionTimewindow) => { setRealtimeSubscription: (subscriptionTimewindow) => {
this.updateRealtimeSubscription(deepClone(subscriptionTimewindow)); if (this.comparisonEnabled && datasource.isAdditional) {
this.updateSubscriptionForComparison(subscriptionTimewindow);
} else {
this.updateRealtimeSubscription(deepClone(subscriptionTimewindow));
}
} }
}; };
this.entityDataListeners.push(listener); this.entityDataListeners.push(listener);
@ -901,7 +931,11 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
const forceUpdate = !this.datasources.length; const forceUpdate = !this.datasources.length;
this.entityDataListeners.forEach((listener) => { this.entityDataListeners.forEach((listener) => {
listener.subscriptionTimewindow = this.subscriptionTimewindow; if (this.comparisonEnabled && listener.configDatasource.isAdditional) {
listener.subscriptionTimewindow = this.timewindowForComparison;
} else {
listener.subscriptionTimewindow = this.subscriptionTimewindow;
}
this.ctx.entityDataService.startSubscription(listener); this.ctx.entityDataService.startSubscription(listener);
}); });
if (forceUpdate) { if (forceUpdate) {
@ -1088,9 +1122,50 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
} }
} }
if (subscriptionsChanged && this.hasDataPageLink) {
subscriptionsChanged = false;
this.updateDataSubscriptions();
}
return subscriptionsChanged; return subscriptionsChanged;
} }
private updateDataSubscriptions() {
this.configuredDatasources = this.ctx.utils.validateDatasources(this.options.datasources);
if (!this.ctx.aliasController) {
this.hasResolvedData = true;
this.prepareDataSubscriptions().subscribe(
() => {
this.updatePaginatedDataSubscriptions();
}
);
} else {
this.ctx.aliasController.resolveDatasources(this.configuredDatasources, this.singleEntity).subscribe(
(datasources) => {
this.configuredDatasources = datasources;
this.prepareDataSubscriptions().subscribe(
() => {
this.updatePaginatedDataSubscriptions();
}
);
},
(err) => {
this.notifyDataLoaded();
}
);
}
}
private updatePaginatedDataSubscriptions() {
for (let datasourceIndex = 0; datasourceIndex < this.entityDataListeners.length; datasourceIndex++) {
const entityDataListener = this.entityDataListeners[datasourceIndex];
if (entityDataListener) {
const pageLink = entityDataListener.subscription.entityDataSubscriptionOptions.pageLink;
const keyFilters = entityDataListener.subscription.entityDataSubscriptionOptions.keyFilters;
this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters);
}
}
}
isDataResolved(): boolean { isDataResolved(): boolean {
return this.hasResolvedData; return this.hasResolvedData;
} }
@ -1126,7 +1201,7 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
} }
private updateRealtimeSubscription(subscriptionTimewindow?: SubscriptionTimewindow) { private updateRealtimeSubscription(subscriptionTimewindow?: SubscriptionTimewindow): SubscriptionTimewindow {
if (subscriptionTimewindow) { if (subscriptionTimewindow) {
this.subscriptionTimewindow = subscriptionTimewindow; this.subscriptionTimewindow = subscriptionTimewindow;
} else { } else {
@ -1149,11 +1224,15 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
} }
private updateSubscriptionForComparison() { private updateSubscriptionForComparison(subscriptionTimewindow?: SubscriptionTimewindow): SubscriptionTimewindow {
if (!this.subscriptionTimewindow) { if (subscriptionTimewindow) {
this.subscriptionTimewindow = this.updateRealtimeSubscription(); this.timewindowForComparison = subscriptionTimewindow;
} else {
if (!this.subscriptionTimewindow) {
this.subscriptionTimewindow = this.updateRealtimeSubscription();
}
this.timewindowForComparison = createTimewindowForComparison(this.subscriptionTimewindow, this.timeForComparison);
} }
this.timewindowForComparison = createTimewindowForComparison(this.subscriptionTimewindow, this.timeForComparison);
this.updateComparisonTimewindow(); this.updateComparisonTimewindow();
return this.timewindowForComparison; return this.timewindowForComparison;
} }
@ -1238,13 +1317,29 @@ export class WidgetSubscription implements IWidgetSubscription {
let index = 0; let index = 0;
this.datasources.forEach((datasource) => { this.datasources.forEach((datasource) => {
datasource.dataKeys.forEach((dataKey) => { datasource.dataKeys.forEach((dataKey) => {
if (datasource.generated) { if (datasource.generated || datasource.isAdditional) {
dataKey._hash = Math.random(); dataKey._hash = Math.random();
dataKey.color = this.ctx.utils.getMaterialColor(index); dataKey.color = this.ctx.utils.getMaterialColor(index);
} }
index++; index++;
}); });
}); });
if (this.comparisonEnabled) {
this.datasourcePages.forEach(datasourcePage => {
datasourcePage.data.forEach((datasource, dIndex) => {
if (datasource.isAdditional) {
const origDatasource = this.datasourcePages[datasource.origDatasourceIndex].data[dIndex];
datasource.dataKeys.forEach((dataKey) => {
if (dataKey.settings.comparisonSettings.color) {
dataKey.color = dataKey.settings.comparisonSettings.color;
}
const origDataKey = origDatasource.dataKeys[dataKey.origDataKeyIndex];
origDataKey.settings.comparisonSettings.color = dataKey.color;
});
}
});
});
}
if (this.displayLegend) { if (this.displayLegend) {
this.legendData.keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); this.legendData.keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label));
} }
@ -1260,8 +1355,17 @@ export class WidgetSubscription implements IWidgetSubscription {
return datasource.dataKeys.map((dataKey, keyIndex) => { return datasource.dataKeys.map((dataKey, keyIndex) => {
dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false; dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false;
dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true; dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true;
dataKey.pattern = dataKey.label; if (this.comparisonEnabled && dataKey.isAdditional) {
dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern); if (dataKey.settings.comparisonSettings.comparisonValuesLabel) {
dataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel);
} else {
dataKey.label = dataKey.label + ' ' + this.ctx.translate.instant('legend.comparison-time-ago.' + this.timeForComparison);
}
dataKey.pattern = dataKey.label;
} else {
dataKey.pattern = dataKey.label;
dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern);
}
const datasourceData: DatasourceData = { const datasourceData: DatasourceData = {
datasource, datasource,
dataKey, dataKey,

4
ui-ngx/src/app/core/services/utils.service.ts

@ -325,7 +325,7 @@ export class UtilsService {
return dataKey; return dataKey;
} }
public createAdditionalDataKey(dataKey: DataKey, datasource: Datasource, timeUnit: string, /* public createAdditionalDataKey(dataKey: DataKey, datasource: Datasource, timeUnit: string,
datasources: Datasource[], additionalKeysNumber: number): DataKey { datasources: Datasource[], additionalKeysNumber: number): DataKey {
const additionalDataKey = deepClone(dataKey); const additionalDataKey = deepClone(dataKey);
if (dataKey.settings.comparisonSettings.comparisonValuesLabel) { if (dataKey.settings.comparisonSettings.comparisonValuesLabel) {
@ -343,7 +343,7 @@ export class UtilsService {
} }
additionalDataKey._hash = Math.random(); additionalDataKey._hash = Math.random();
return additionalDataKey; return additionalDataKey;
} }*/
public createLabelFromDatasource(datasource: Datasource, pattern: string): string { public createLabelFromDatasource(datasource: Datasource, pattern: string): string {
return createLabelFromDatasource(datasource, pattern); return createLabelFromDatasource(datasource, pattern);

3
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -156,7 +156,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.pageLink = { this.pageLink = {
page: 0, page: 0,
pageSize: this.defaultPageSize, pageSize: this.defaultPageSize,
textSearch: null textSearch: null,
dynamic: true
}; };
// new PageLink(this.defaultPageSize, 0, null, sortOrder); // new PageLink(this.defaultPageSize, 0, null, sortOrder);
} }

3
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -92,6 +92,7 @@ import { EntityService } from '@core/http/entity.service';
import { ServicesMap } from '@home/models/services.map'; import { ServicesMap } from '@home/models/services.map';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { EntityDataService } from '@core/api/entity-data.service'; import { EntityDataService } from '@core/api/entity-data.service';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
selector: 'tb-widget', selector: 'tb-widget',
@ -165,6 +166,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
private dashboardService: DashboardService, private dashboardService: DashboardService,
// private datasourceService: DatasourceService, // private datasourceService: DatasourceService,
private entityDataService: EntityDataService, private entityDataService: EntityDataService,
private translate: TranslateService,
private utils: UtilsService, private utils: UtilsService,
private raf: RafService, private raf: RafService,
private ngZone: NgZone, private ngZone: NgZone,
@ -295,6 +297,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.subscriptionContext.timeService = this.timeService; this.subscriptionContext.timeService = this.timeService;
this.subscriptionContext.deviceService = this.deviceService; this.subscriptionContext.deviceService = this.deviceService;
this.subscriptionContext.alarmService = this.alarmService; this.subscriptionContext.alarmService = this.alarmService;
this.subscriptionContext.translate = this.translate;
// this.subscriptionContext.datasourceService = this.datasourceService; // this.subscriptionContext.datasourceService = this.datasourceService;
this.subscriptionContext.entityDataService = this.entityDataService; this.subscriptionContext.entityDataService = this.entityDataService;
this.subscriptionContext.utils = this.utils; this.subscriptionContext.utils = this.utils;

1
ui-ngx/src/app/shared/models/query/query.models.ts

@ -138,6 +138,7 @@ export interface EntityDataPageLink {
page: number; page: number;
textSearch?: string; textSearch?: string;
sortOrder?: EntityDataSortOrder; sortOrder?: EntityDataSortOrder;
dynamic?: boolean;
} }
export function entityDataPageLinkSortDirection(pageLink: EntityDataPageLink): SortDirection { export function entityDataPageLinkSortDirection(pageLink: EntityDataPageLink): SortDirection {

2
ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts

@ -136,6 +136,7 @@ export interface EntityHistoryCmd {
interval: number; interval: number;
limit: number; limit: number;
agg: AggregationType; agg: AggregationType;
fetchLatestPreviousPoint?: boolean;
} }
export interface LatestValueCmd { export interface LatestValueCmd {
@ -149,6 +150,7 @@ export interface TimeSeriesCmd {
interval: number; interval: number;
limit: number; limit: number;
agg: AggregationType; agg: AggregationType;
fetchLatestPreviousPoint?: boolean;
} }
export class EntityDataCmd implements WebsocketCmd { export class EntityDataCmd implements WebsocketCmd {

3
ui-ngx/src/app/shared/models/widget.models.ts

@ -235,6 +235,8 @@ export interface DataKey extends KeyInfo {
usePostProcessing?: boolean; usePostProcessing?: boolean;
hidden?: boolean; hidden?: boolean;
inLegend?: boolean; inLegend?: boolean;
isAdditional?: boolean;
origDataKeyIndex?: number;
_hash?: number; _hash?: number;
} }
@ -266,6 +268,7 @@ export interface Datasource {
entityDescription?: string; entityDescription?: string;
generated?: boolean; generated?: boolean;
isAdditional?: boolean; isAdditional?: boolean;
origDatasourceIndex?: number;
pageLink?: EntityDataPageLink; pageLink?: EntityDataPageLink;
keyFilters?: Array<KeyFilter>; keyFilters?: Array<KeyFilter>;
entityFilter?: EntityFilter; entityFilter?: EntityFilter;

Loading…
Cancel
Save