Refactored loading of channels in (XY-)chartviewer Change-Id: I8989a9be91a1b1d441c010e0328c2fa8b293f299
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.ts index 0cb2299..48dd678 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.ts
@@ -22,6 +22,9 @@ import { Node } from '../../../navigator/node'; import { ChartViewerDataService, getDataArray } from '../../services/chart-viewer-data.service'; import { MeasuredValues } from '../../model/chartviewer.model'; +import { ChannelGroup } from '../../model/types/channelgroup.class'; +import { Measurement } from '../../model/types/measurement.class'; +import { Channel } from '../../model/types/channel.class'; @Component({ selector: 'mdm5-chartViewer', @@ -31,11 +34,7 @@ export class ChartViewerComponent implements OnChanges { @Input() - channelGroup = new Node(); - @Input() - channels = [new Node()]; - @Input() - channelGroupMap: Map<Node, Node[]> = new Map(); + measurement: Measurement; @ViewChild('lineChart') chart: UIChart; @@ -91,10 +90,9 @@ this.data = this.getEmptyData(); this.tmpData = this.getEmptyData(); for (const propName in changes) { - if (this.channelGroupMap !== undefined - && (propName === 'channelGroupMap' || propName === 'channelGroup' || propName === 'channels')) { - this.channelGroupMap.forEach((channels, group) => { - this.numberOfRows += this.chartService.getNumberOfRows(group); + if (this.measurement !== undefined && (propName === 'measurement')) { + this.measurement.channelGroups.forEach(group => { + this.numberOfRows += group.numberOfRows; if (this.numberOfRows < this.numberOfChunks) { this.previewEnabled = false; } @@ -105,16 +103,6 @@ }); break; - } else if (this.channelGroup !== undefined && (propName === 'channelGroup' || propName === 'channels')) { - this.numberOfRows += this.chartService.getNumberOfRows(this.channelGroup); - if (this.numberOfRows < this.numberOfChunks) { - this.previewEnabled = false; - } - this.requestedValues = Math.min(this.numberOfRows, this.maxRequestedValues); - - this.updateRange(); - this.chartChannel(undefined); - break; } } } @@ -140,17 +128,18 @@ this.chartChannel(undefined); } - chartChannel(channelGroup: Node) { + chartChannel(channelGroup: ChannelGroup) { + let group: ChannelGroup; if (channelGroup) { - this.chartMeasuredValues(this.chartService.loadValues( - channelGroup, this.channelGroupMap.get(channelGroup), - this.rangeValues[0] - 1, this.rangeValues[1], this.previewEnabled ? this.numberOfChunks : 0)); + group = channelGroup; } else { // either the selected channel or all channels from the map - this.chartMeasuredValues(this.chartService.loadValues( - this.channelGroup, this.channels.length === 1 ? this.channels : this.channelGroupMap.get(this.channelGroup), - this.rangeValues[0] - 1, this.rangeValues[1], this.previewEnabled ? this.numberOfChunks : 0)); + group = this.measurement.getFirstChannelGroup(); } + + this.chartMeasuredValues(this.chartService.loadValues( + channelGroup, this.measurement.findChannels(channelGroup), + this.rangeValues[0] - 1, this.rangeValues[1], this.previewEnabled ? this.numberOfChunks : 0)); } chartMeasuredValues(measuredValues: Observable<MeasuredValues[]>) { @@ -163,7 +152,6 @@ }), catchError(this.httpErrorHandler.handleError) ).subscribe(d => { - console.log(d); if (this.tmpData.labels.length === 0) { this.tmpData = d.data; } else {
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.html index c110d72..86c5175 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.html +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.html
@@ -12,11 +12,11 @@ * ********************************************************************************--> -<div *ngIf="channels === undefined"> +<div *ngIf="measurement === undefined"> No Channel selected. </div> -<div *ngIf="channels !== undefined"> +<div *ngIf="measurement !== undefined"> <div class="p-grid toolbar"> <div class="thin"> <p-selectButton [options]="modes" [(ngModel)]="selectedMode"></p-selectButton> @@ -27,8 +27,8 @@ </div> </div> <div [ngSwitch]="selectedMode"> - <mdm5-chartViewer *ngSwitchCase="'chart'" [channelGroup]="channelGroup" [channels]="channels" [channelGroupMap]="channelGroupMap"></mdm5-chartViewer> - <mdm5-dataTable *ngSwitchCase="'table'" [channels]="channels" [channelGroupMap]="channelGroupMap"></mdm5-dataTable> + <mdm5-chartViewer *ngSwitchCase="'chart'" [measurement]="measurement"></mdm5-chartViewer> + <mdm5-dataTable *ngSwitchCase="'table'" [measurement]="measurement"></mdm5-dataTable> <div *ngSwitchDefault></div> </div> </div>
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.ts index c03be10..dd7979c 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chatviewer-nav-card/chart-viewer-nav-card.component.ts
@@ -25,6 +25,8 @@ import { ChartViewerDataService } from '../../services/chart-viewer-data.service'; import { Query, QueryService, Row, SearchResult } from '../../../tableview/query.service'; import { map, switchMap } from 'rxjs/operators'; +import { Measurement } from '../../model/types/measurement.class'; +import { TYPE_CHANNEL, TYPE_CHANNELGROUP, TYPE_MEASUREMENT } from '../../model/constants'; @Component({ selector: 'mdm5-chartViewerNavCard', @@ -33,23 +35,15 @@ providers: [] }) export class ChartViewerNavCardComponent implements OnInit, OnDestroy { - private _contextUrl: string; - subscription: any; modes: SelectItem[] = []; selectedMode = 'chart'; // public data for sub-components selectedNode: Node; - channelGroup: Node; - channels: Node[]; - channelGroupMap: Map<Node, Node[]>; + cachedMeasurement: Measurement; + measurement: Measurement; - // temporary data for dynamic reloading - tmpChannelGroupMap: Map<Node, Node[]>; - foundChannels: Node[]; - - cachedChannelGroups: Map<Node, Node[]>; totalCachedChannels = 0; // dynamic reloading @@ -57,7 +51,7 @@ offset = 0; loadInterval = 10; allLoaded = false; - openChannels = { amount: '' }; + openChannels = { limit: 0, offset: 0, total: 0 }; constructor(private navigatorService: NavigatorService, private notificationService: MDMNotificationService, @@ -66,9 +60,7 @@ private _prop: PropertyService, private preferenceService: PreferenceService, private chartService: ChartViewerDataService, - private queryService: QueryService) { - this._contextUrl = _prop.getUrl('mdm/environments'); - } + private queryService: QueryService) {} ngOnInit() { this.modes = [ @@ -100,233 +92,54 @@ if (!node) { return; } - this.selectedNode = node; - - this.cleanData(); - this.loadData(node, this.limit, false); + if (node.type === TYPE_MEASUREMENT || node.type === TYPE_CHANNELGROUP || node.type === TYPE_CHANNEL) { + this.selectedNode = node; + this.cleanData(); + this.loadData(node, this.limit, false); + } else { + this.selectedNode = undefined; + this.cleanData(); + } } cleanData() { - this.channels = undefined; - this.channelGroup = undefined; - this.channelGroupMap = new Map(); - this.tmpChannelGroupMap = new Map(); - this.cachedChannelGroups = new Map(); + this.cachedMeasurement = new Measurement(); this.totalCachedChannels = 0; - this.foundChannels = []; + this.measurement = undefined; this.offset = 0; - this.openChannels = { amount: '' }; + this.openChannels = { limit: 0, offset: 0, total: 0 }; this.allLoaded = false; } loadData(node: Node, limit: number, join: boolean) { - if (join) { - this.tmpChannelGroupMap = new Map(); - } - if (node && node.type === 'Channel') { - this.loadChannelGroup(node) - .subscribe(channelGroup => { - if (channelGroup) { - const tmp = new Node(); - tmp.sourceName = node.sourceName; - tmp.type = 'ChannelGroup'; - tmp.sourceType = 'SubMatrix'; - tmp.id = channelGroup.id; - const attr : Attribute = new Attribute(); - attr.name = 'SubMatrixNoRows'; - attr.value = channelGroup.numberOfRows; - tmp.attributes = [attr]; - - this.foundChannels = undefined; - this.tmpChannelGroupMap.set(tmp, [node]); - this.channelGroupMap = new Map(this.tmpChannelGroupMap); - this.channelGroup = tmp; - this.channels = [node]; - } - }); - } else if (node && node.type === 'ChannelGroup') { - if (!join) { - // initially load all channels and store them locally - // add the first batch to the limit - this.chartService.loadChannelsForGroup(node) - .subscribe(searchResult => { - // process the results and transform to nodes - const tmpChannels = []; - for (let i = 0; i < searchResult.rows.length; i++) { - const tmp = new Node(); - tmp.sourceName = node.sourceName; - tmp.type = searchResult.rows[i].type; - for (let j = 0; j < searchResult.rows[i].columns.length; j++) { - if (searchResult.rows[i].columns[j].attribute === 'Id' && searchResult.rows[i].columns[j].type === 'Channel') { - tmp.id = searchResult.rows[i].columns[j].value; - } - if (searchResult.rows[i].columns[j].attribute === 'Name' && searchResult.rows[i].columns[j].type === 'Channel') { - tmp.name = searchResult.rows[i].columns[j].value; - } - } - tmpChannels.push(tmp); - } - this.totalCachedChannels += tmpChannels.length; - this.cachedChannelGroups.set(node, tmpChannels); - this.displayNextChannels(); - }); - } else { - this.displayNextChannels(); - } - } else if (node && node.type === 'Measurement') { - if (!join) { - this.loadSubsequentChannelGroups(node).subscribe((channelGroups) => { - this.processMeasurement(channelGroups); - }); - } else { - this.displayNextChannels(); - } - } + this.chartService.loadMeasurement(node).subscribe(measurement => { + this.cachedMeasurement = measurement; + this.displayNextChannels(); + }); } displayNextChannels() { - const keys = this.cachedChannelGroups.keys(); - let key: IteratorResult<Node> = keys.next(); + let m = new Measurement(this.cachedMeasurement); - let processed = 0; - let loaded = 0; - let dynamicOffset = this.offset; - const tmpChannels: Node[] = []; - - while (key !== null && key.value !== null && !key.done) { - // the internal cache - const channels = this.cachedChannelGroups.get(key.value); - if (channels) { - - let start: number = dynamicOffset; - if (processed > 0 && start > processed) { - start -= processed; - } - - let end: number = this.limit + dynamicOffset; - if (processed > 0 && end > processed) { - end -= processed; - } - if (end > channels.length) { - end = channels.length; - } - - // loaded is only affected on a pagination between multiple groups - start -= loaded; - end -= loaded; - - if (start < 0) { - start = 0; - } - if (end < start) { - end = start; - } - - if (start > 0) { - if (start > channels.length) { - this.tmpChannelGroupMap.set(key.value, []); - } else { - loaded += end - start; - let tmpChnls = this.channelGroupMap.get(key.value); - const tmpChnls2 = channels.slice(start, end); - if (!tmpChnls) { - tmpChnls = []; - } - for (let j = 0; j < tmpChnls2.length; j++) { - tmpChnls.push(tmpChnls2[j]); - } - this.tmpChannelGroupMap.set(key.value, tmpChnls); - } - // follow-up iterations start always at zero if the offset is lower than the channel size - if (channels.length > dynamicOffset) { - dynamicOffset = 0; - } - } else { - // no offset, just limit - loaded += (end - start); - this.tmpChannelGroupMap.set(key.value, channels.slice(start, end)); - } - processed += (channels.length); - } - - key = keys.next(); - - if (loaded >= this.loadInterval) { - // load interval reached for all groups, cancel processing - key = null; + if (this.measurement) { + // Added already selected channels + for (let channel of this.measurement.allChannels()) { + m.put(this.measurement.findChannelGroupByChannel(channel), channel); } } - this.channelGroupMap = new Map(this.tmpChannelGroupMap); - this.channels = tmpChannels; - this.offset += loaded; + + let channels = Array.from(this.cachedMeasurement.allChannels()); + let loadedChannels = channels.slice(this.offset, this.offset + this.limit); + loadedChannels.forEach(c => m.put(this.cachedMeasurement.findChannelGroupByChannel(c), c)); + + this.totalCachedChannels = channels.length; + this.offset += loadedChannels.length; this.allLoaded = this.offset >= this.totalCachedChannels; - this.openChannels = { amount: (this.loadInterval).toString() + '/' + (this.totalCachedChannels).toString() }; - } - - processMeasurement(channelGroups: Node[]) { - for (let i = 0; i < channelGroups.length; i++) { - const node = channelGroups[i]; - this.chartService.loadChannelsForGroup(node) - .subscribe(searchResult => { - // process the results and transform to nodes - const tmpChannels = []; - for (let j = 0; j < searchResult.rows.length; j++) { - const tmp = new Node(); - tmp.sourceName = node.sourceName; - tmp.type = searchResult.rows[j].type; - for (let k = 0; k < searchResult.rows[j].columns.length; k++) { - if (searchResult.rows[j].columns[k].attribute === 'Id' && searchResult.rows[j].columns[k].type === 'Channel') { - tmp.id = searchResult.rows[j].columns[k].value; - } - if (searchResult.rows[j].columns[k].attribute === 'Name' && searchResult.rows[j].columns[k].type === 'Channel') { - tmp.name = searchResult.rows[j].columns[k].value; - } - } - tmpChannels.push(tmp); - } - this.totalCachedChannels += tmpChannels.length; - this.cachedChannelGroups.set(node, tmpChannels); - if (i + 1 === channelGroups.length) { - this.displayNextChannels(); - } - }); - } + this.openChannels = { limit: this.loadInterval, offset: this.offset, total: this.totalCachedChannels }; + this.measurement = m; } loadMissingChannels(event) { - this.loadData(this.selectedNode, this.limit, true); + this.displayNextChannels(); } - - loadSubsequentChannelGroups(measurement: Node) { - const url = this._contextUrl + '/' + measurement.sourceName + '/channelgroups?filter=Measurement.Id eq "' + measurement.id + '"'; - return this.nodeService.getNode(url); - } - - loadChannelGroup(channel: Node): Observable<{id: string, numberOfRows: string}> { - const query = new Query(); - query.addFilter(channel.sourceName, 'Channel.Id eq ' + channel.id); - query.columns = ['ChannelGroup.Id', 'ChannelGroup.SubMatrixNoRows']; - query.resultType = 'ChannelGroup'; - - return this.queryService.query(query).pipe( - map(sr => sr.rows.map(r => { return { id: r.id, numberOfRows: r.getColumn('ChannelGroup.SubMatrixNoRows') }; })), - switchMap(e => (e.length === 1) ? of(e[0]) : throwError('Channel ' + channel.id + ' must be in exactly 1 ChannelGroup.')) - ); - } - - loadChannels(channelgroup: Node, limit?: number, offset?: number): Observable<Node[]> { - let url = this._contextUrl + '/' + channelgroup.sourceName + '/channels?filter=ChannelGroup.Id eq "' + channelgroup.id + '"'; - if (limit) { - url = url + '&limit=' + limit; - } - if (offset) { - url = url + '&offset=' + offset; - } - return this.nodeService.getNode(url); - } - - queryRowsForGroup(channelGroup: Node): Observable<SearchResult> { - return this.chartService.loadChannelsForGroup(channelGroup); - } - }
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/datatable/data-table.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/datatable/data-table.component.ts index e73cc55..df8dec9 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/datatable/data-table.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/datatable/data-table.component.ts
@@ -20,6 +20,7 @@ import { Node } from '../../../navigator/node'; import { ChartViewerDataService, getDataArray } from '../../services/chart-viewer-data.service'; import { MeasuredValues } from '../../model/chartviewer.model'; +import { Measurement } from '../../model/types/measurement.class'; @Component({ selector: 'mdm5-dataTable', @@ -31,15 +32,8 @@ @ViewChild('datatable') private table: Table; - // xy-chart-viewer or single channel node @Input() - channels = [new Node()]; - // xy-chart-viewer - @Input() - channelGroups = []; - // chart-viewer - @Input() - channelGroupMap: Map<Node, Node[]>; + measurement: Measurement; tableLoading = false; cols: Column[] = []; @@ -52,7 +46,7 @@ ngOnChanges(changes: SimpleChanges) { for (const propName in changes) { - if (propName === 'channelGroup' || propName === 'channels' || propName === 'channelGroupMap') { + if (propName === 'measurement') { this.table.reset(); this.cols = []; this.datapoints = []; @@ -75,41 +69,23 @@ } loadLazy(event: LazyLoadEvent) { - const hasChannelGroups = this.channelGroups !== undefined && this.channelGroups.length > 0; - const hasMap = this.channelGroupMap !== undefined && this.channelGroupMap.size > 0; - if (!hasChannelGroups && !hasMap) { + if (!this.measurement) { return; } - let tmpChannelGroups = this.channelGroups; - - if (!hasChannelGroups && hasMap) { - tmpChannelGroups = []; - this.channels = []; - this.channelGroupMap.forEach((values, key) => { - tmpChannelGroups.push(key); - for (let j = 0; j < values.length; j++) { - this.channels.push(values[j]); - } - }); - } - setTimeout(() => { this.tableLoading = true; - for (const chGrp in tmpChannelGroups) { - if (tmpChannelGroups.hasOwnProperty(chGrp)) { - const tmpChannelGroup = tmpChannelGroups[chGrp]; - const tmpChannels = hasMap && this.channels.length > 1 || this.channels.length === 1 && !this.channels[0].id - ? this.channelGroupMap.get(tmpChannelGroup) : this.channels; - this.totalRecords = this.chartviewerService.getNumberOfRows(tmpChannelGroup); - this.chartviewerService.loadMeasuredValues(tmpChannelGroup, tmpChannels, event.first, event.rows) - .subscribe(mv => { - this.load(mv); - this.tableLoading = false; - }, - () => this.tableLoading = false); - } + for (const channelGroup of this.measurement.channelGroups) { + const channels = this.measurement.findChannels(channelGroup); + + this.totalRecords = channelGroup.numberOfRows; + this.chartviewerService.loadMeasuredValues(channelGroup, channels, event.first, event.rows) + .subscribe(mv => { + this.load(mv); + this.tableLoading = false; + }, + () => this.tableLoading = false); } }, 0); }
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts index ddbf822..556cdac 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts
@@ -16,6 +16,7 @@ import { ChartViewerDataService } from '../../services/chart-viewer-data.service'; import { Node } from '../../../navigator/node'; import { RequestOptions } from '../../model/chartviewer.model'; +import { Measurement } from '../../model/types/measurement.class'; @Component({ selector: 'mdm5-request-options', @@ -38,7 +39,7 @@ public previewEnabled = true; @Input() - public channelGroups: Node[]; + public measurement: Measurement; @Output() public onRequestOptionsChanged = new EventEmitter<RequestOptions>(); @@ -52,15 +53,15 @@ } ngOnChanges(changes: SimpleChanges) { - if (changes['channelGroups']) { + if (changes['measurement']) { this.reload(); this.emitRequestOptionsChanged(); } } private reload() { - if (this.channelGroups != undefined && this.channelGroups.length > 0) { - this.numberOfRows = this.chartService.getNumberOfRows(this.channelGroups[0]); + if (this.measurement != undefined && this.measurement.channelGroups.length > 0) { + this.numberOfRows = this.measurement.getFirstChannelGroup().numberOfRows; this.step = Math.min(1000, Math.max(Math.floor(this.numberOfRows / 100), 1)); if (this.numberOfRows < this.numberOfChunks) { this.previewEnabled = false;
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html index c2cfc55..d91ae6e 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html
@@ -15,7 +15,7 @@ <div class="p-grid"> <div class="p-col-12" style="margin-top: 4px"> <p-listbox - [options]="channelGroups" + [options]="measurement.channelGroups" [(ngModel)]="selectedChannelGroups" multiple="multiple" checkbox="checkbox"
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts index 669a029..31fe6da 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts
@@ -20,11 +20,13 @@ import { ArrayUtilService } from 'src/app/core/services/array-util.service'; import { MDMNotificationService } from 'src/app/core/mdm-notification.service'; import { TYPE_MEASUREMENT, TYPE_CHANNELGROUP, TYPE_CHANNEL } from '../../model/constants'; -import { map, tap } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { Node } from '../../../navigator/node'; import {ConfirmationService} from 'primeng/api'; import { TranslateService } from '@ngx-translate/core'; -import { AxisInfo } from '../../model/types/axis-info.class'; +import { Measurement } from '../../model/types/measurement.class'; +import { ChannelGroup } from '../../model/types/channelgroup.class'; +import { Channel } from '../../model/types/channel.class'; @Component({ selector: 'mdm5-xy-chart-data-selection-panel', @@ -34,9 +36,7 @@ export class XyChartDataSelectionPanelComponent implements OnInit, OnDestroy, OnChanges { @Input() - public channelGroups: Node[]; - @Input() - public channels: Map<string, Node[]>; + public measurement: Measurement; @Input() public xyMode: boolean; @@ -45,20 +45,19 @@ public onSelectionChanged = new EventEmitter<ChannelSelectionRow[]>(); // select options for channels - public yChannelOptions: Node[] = []; - public xChannelOptions: {[key: string]: Node[]} = {}; + public yChannelOptions: Channel[] = []; + public xChannelOptions: {[key: string]: Channel[]} = {}; // channel(group) selection for chart // !! change via setter to update cache for workarround !! - public selectedChannelGroups: Node[] = []; - public selectedYChannels: Node[] = []; + public selectedChannelGroups: ChannelGroup[] = []; + public selectedYChannels: Channel[] = []; - private xYcache = new Map<string, AxisInfo[]>(); private selectionChanged = false; // listbox workarround to determine toggled item(s) - private lastChannelGroupSelection: Node[] = []; - private lastYChannelSelection: Node[] = []; + private lastChannelGroupSelection: ChannelGroup[] = []; + private lastYChannelSelection: Channel[] = []; public selectedChannelRows: ChannelSelectionRow[] = []; @@ -67,7 +66,7 @@ public hiddenYChannels = false; // for x-channel default value - private independentChannels = new Map<string, Node>(); + private independentChannels = new Map<string, Channel>(); /********** subscriptions */ private chartViewerSub: Subscription; @@ -92,12 +91,11 @@ ngOnChanges(changes: SimpleChanges) { if (changes['xyMode']) { - this.reloadAxisSelectOptions().subscribe(() => { - if (this.selectionChanged) { - this.fireSelectionChanged(); - this.selectionChanged = false; - } - }); + this.reloadAxisSelectOptions(); + if (this.selectionChanged) { + this.fireSelectionChanged(); + this.selectionChanged = false; + } } } @@ -128,7 +126,7 @@ // y-axis public onSelectedYChannelsChanged(event: any) { - let channel: Node; + let channel: Channel; // Deselect if (this.selectedYChannels.length < this.lastYChannelSelection.length) { channel = this.lastYChannelSelection.find(yChannel => !this.selectedYChannels.some(c => yChannel.id === c.id)); @@ -137,7 +135,7 @@ } else { channel = this.selectedYChannels.find(group => !this.lastYChannelSelection.some(g => group.id === g.id)); } - this.addOrRemoveRow(this.findChannelGroup(channel), channel); + this.addOrRemoveRow(this.measurement.findChannelGroupByChannel(channel), channel); this.lastYChannelSelection = this.selectedYChannels; this.fireSelectionChanged(); } @@ -171,65 +169,56 @@ this.hiddenYChannels = !this.hiddenYChannels; } - private handleDeselectChannelGroup(channelGroup: Node) { + private handleDeselectChannelGroup(channelGroup: ChannelGroup) { // remove rows if (this.arrayUtil.isNotEmpty(this.selectedChannelRows)) { this.selectedChannelRows = this.selectedChannelRows.filter(row => row.channelGroup.id !== channelGroup.id); } // remove selection if (this.arrayUtil.isNotEmpty(this.selectedYChannels)) { - this.setSelectedYChannels(this.selectedYChannels.filter(channel => - this.channels.get(channelGroup.id).findIndex(c => channel.id === c.id) === -1)); + this.setSelectedYChannels(this.selectedYChannels.filter(channel => this.measurement.hasChannel(channel.id))); + // this.channels.get(channelGroup.id).findIndex(c => channel.id === c.id) === -1)); } - this.reloadAxisSelectOptions().subscribe(() => this.fireSelectionChanged()); + this.reloadAxisSelectOptions(); + this.fireSelectionChanged(); } /** * Handle channelGroup selection via multiselect in html template * @param channelGroup */ - private handleSelectChannelGroup(channelGroup: Node) { - const cached = this.xYcache.get(channelGroup.id); - if (cached != undefined) { - this.setChannelOptions(cached, channelGroup); - this.fireSelectionChanged(); - } else { - this.chartviewerDataService.loadAxisInformation(channelGroup) - .subscribe(ai => { - this.setChannelOptions(ai, channelGroup); - this.fireSelectionChanged(); - }); - } + private handleSelectChannelGroup(channelGroup: ChannelGroup) { + this.setChannelOptions(channelGroup); + this.fireSelectionChanged(); } private handleNodeSelectionInNavTree(node: Node) { if (node != undefined) { switch (node.type) { case TYPE_MEASUREMENT: - this.xYcache.clear(); this.resetChannelData(); this.fireSelectionChanged(); break; case TYPE_CHANNELGROUP: this.cleanOldState(); - this.addChannelGroupToSelection(node.id, node); + this.addChannelGroupToSelection(node.id, this.measurement.findChannelGroup(node.id)); break; case TYPE_CHANNEL: this.cleanOldState(); + const channel = this.measurement.findChannel(node.id); + const channelGroup = this.measurement.findChannelGroupByChannel(channel); // add related channel group to selection - if (this.arrayUtil.isNotEmpty(this.channelGroups) && this.channels != undefined) { - const channelGroup = this.findChannelGroup(node); + if (channel !== undefined && channelGroup !== undefined) { + const c = this.measurement.findChannel(node.id); + const cg = this.measurement.findChannelGroupByChannel(c); // this.addChannelGroupToSelection(channelGroupId, this.channelGroups.find(cg => cg.id === channelGroupId)); - if (!this.isNodeIn(channelGroup, this.selectedChannelGroups)) { - this.setSelectedChannelGroups(this.selectedChannelGroups.concat([channelGroup])); - this.chartviewerDataService.loadAxisInformation(channelGroup) - .subscribe(mvls => { - this.setChannelOptions(mvls, channelGroup); - this.addChannelToSelection(node, channelGroup); - }); + if (!this.isChannelGroupIn(cg, this.selectedChannelGroups)) { + this.setSelectedChannelGroups(this.selectedChannelGroups.concat([cg])); + this.setChannelOptions(cg); + this.addChannelToSelection(c, cg); } else { // if channel is in axis type y, set as selected, if not selected already - this.addChannelToSelection(node, channelGroup); + this.addChannelToSelection(c, cg); } } break; @@ -246,8 +235,7 @@ */ private cleanOldState() { if (this.arrayUtil.isNotEmpty(this.selectedChannelGroups) - && this.selectedChannelGroups.some(selected => !this.isNodeIn(selected, this.channelGroups))) { - this.xYcache.clear(); + && this.selectedChannelGroups.some(selected => this.measurement.findChannelGroup(selected.id) === undefined)) { this.resetChannelData(); } } @@ -257,9 +245,9 @@ * @param channel * @param channelGroup */ - private addChannelToSelection(channel: Node, channelGroup: Node) { + private addChannelToSelection(channel: Channel, channelGroup: ChannelGroup) { // adds channel to selected y-channels if possible and creates row in table - if (this.isNodeIn(channel, this.yChannelOptions)) { + if (this.isChannelIn(channel, this.yChannelOptions)) { const index = this.selectedYChannels.findIndex(c => c.id === channel.id); if (index === -1) { this.setSelectedYChannels(this.selectedYChannels.concat([channel])); @@ -271,7 +259,7 @@ * suggestion: set it as x-channel for all selected y-channels of that channelgroup. */ // if channel is of axis type x, set ... ? - } else if (this.isNodeIn(channel, this.xChannelOptions[channelGroup.id])) { + } else if (this.isChannelIn(channel, this.xChannelOptions[channelGroup.id])) { this.notificationService.notifyWarn('Selected X-Channel', 'Selected channel is of type x-axis. Change x/y mode.'); } else { this.notificationService.notifyError('Unknown channel option', 'Channel ' + channel.name + ' has no axis.'); @@ -283,11 +271,11 @@ * @param id * @param channelGroup */ - private addChannelGroupToSelection(id: string, channelGroup: Node) { + private addChannelGroupToSelection(id: string, channelGroup: ChannelGroup) { if (this.selectedChannelGroups == undefined) { this.setSelectedChannelGroups([]); } - if (!this.isNodeIn(channelGroup, this.selectedChannelGroups)) { + if (!this.isChannelGroupIn(channelGroup, this.selectedChannelGroups)) { this.setSelectedChannelGroups(this.selectedChannelGroups.concat([channelGroup])); this.handleSelectChannelGroup(channelGroup); } @@ -299,38 +287,32 @@ * @param axisInfos * @param channelGroup */ - private setChannelOptions(axisInfos: AxisInfo[], channelGroup: Node) { + private setChannelOptions(channelGroup: ChannelGroup) { if (channelGroup != undefined) { - // cache axisInfos to avoid reloading - this.xYcache.set(channelGroup.id, axisInfos); - // create empty array if property not exists this.xChannelOptions[channelGroup.id] = this.xChannelOptions[channelGroup.id] || []; if (this.xyMode) { - axisInfos.forEach(ai => { - const tmpChannel = this.channels.get(channelGroup.id).filter(c => c.name === ai.name)[0]; - if (ai.axisType === 'X_AXIS' || ai.axisType === 'XY_AXIS' || ( ai.axisType == undefined && ai.independent)) { - this.xChannelOptions[channelGroup.id] = this.xChannelOptions[channelGroup.id].concat(tmpChannel); - // cache independent channels for default x-axis. - if (ai.independent) { - this.independentChannels.set(channelGroup.id, tmpChannel); + this.measurement.findChannels(channelGroup).forEach(channel => { + if (channel.axisType === 'X_AXIS' || channel.axisType === 'XY_AXIS' || (channel.axisType == undefined && channel.isIndependent)) { + this.xChannelOptions[channelGroup.id] = this.xChannelOptions[channelGroup.id].concat(channel); + if (channel.isIndependent) { + this.independentChannels.set(channelGroup.id, channel); } - } else if (ai.axisType === 'Y_AXIS' || ai.axisType === 'XY_AXIS') { - this.yChannelOptions = this.yChannelOptions.concat(tmpChannel); + } else if (channel.axisType === 'Y_AXIS' || channel.axisType === 'XY_AXIS') { + this.yChannelOptions = this.yChannelOptions.concat(channel); } }); } else { - // cache independent channel for default x-axis. - const value = axisInfos.find(ai => ai.independent); - if (value != undefined) { - const tmpChannel = this.channels.get(channelGroup.id).filter(c => c.name === value.name)[0]; - this.independentChannels.set(channelGroup.id, tmpChannel); + let channel = this.measurement.getIndependentChannel(channelGroup); + if (channel) { + this.independentChannels.set(channelGroup.id, channel); } - const tmpChannels = this.channels.get(channelGroup.id); - this.xChannelOptions[channelGroup.id] = this.xChannelOptions[channelGroup.id].concat(tmpChannels); - this.yChannelOptions = this.yChannelOptions.concat(tmpChannels); + let channels = this.measurement.findChannels(channelGroup); + + this.xChannelOptions[channelGroup.id] = this.xChannelOptions[channelGroup.id].concat(channels); + this.yChannelOptions = this.yChannelOptions.concat(channels); } } } @@ -342,7 +324,6 @@ this.setSelectedChannelGroups([]); this.selectedChannelRows = []; this.setSelectedYChannels([]); - this.channelGroups = [].concat(this.channelGroups); this.xChannelOptions = {}; this.yChannelOptions = []; } @@ -354,18 +335,19 @@ this.xChannelOptions = {}; this.yChannelOptions = []; if (this.arrayUtil.isNotEmpty(this.selectedChannelGroups)) { - const cached = this.selectedChannelGroups.filter(g => this.xYcache.get(g.id) !== undefined); - return of(cached.map((group, index) => this.setChannelOptions(this.xYcache.get(group.id), cached[index]))) - .pipe(tap(() => this.removeSelectionsNotMatchingXYMode())); + + this.selectedChannelGroups.forEach(cg => { + this.setChannelOptions(cg); + }); + this.removeSelectionsNotMatchingXYMode(); } - return of(); } private removeSelectionsNotMatchingXYMode() { if (this.selectedYChannels.length > 0) { const l = this.selectedChannelRows.length; - this.selectedChannelRows = this.selectedChannelRows.filter(row => this.isNodeIn(row.yChannel, this.yChannelOptions)); - this.setSelectedYChannels(this.selectedYChannels.filter(channel => this.isNodeIn(channel, this.yChannelOptions))); + this.selectedChannelRows = this.selectedChannelRows.filter(row => this.isChannelIn(row.yChannel, this.yChannelOptions)); + this.setSelectedYChannels(this.selectedYChannels.filter(channel => this.isChannelIn(channel, this.yChannelOptions))); this.selectionChanged = l !== this.selectedChannelRows.length; } } @@ -376,7 +358,7 @@ * @param channelGroup * @param yChannel */ - private addOrRemoveRow(channelGroup: Node, yChannel: Node) { + private addOrRemoveRow(channelGroup: ChannelGroup, yChannel: Channel) { const index = this.selectedChannelRows.findIndex(row => row.yChannel.id === yChannel.id); if (index > -1) { this.selectedChannelRows.splice(index, 1); @@ -391,7 +373,7 @@ * @param channelGroup * @param yChannel */ - private addRow(channelGroup: Node, yChannel: Node) { + private addRow(channelGroup: ChannelGroup, yChannel: Channel) { const row = new ChannelSelectionRow(channelGroup, yChannel, this.getDefaultXChannel(channelGroup)); this.selectedChannelRows.push(row); } @@ -401,7 +383,9 @@ * if non is found the first channel with axistype X-Axis or XY-Axis is returned. * @param channelGroup */ - private getDefaultXChannel(channelGroup: Node) { + private getDefaultXChannel(channelGroup: ChannelGroup) { + this.measurement.getIndependentChannel(channelGroup); + let defaultXChannel = this.independentChannels.get(channelGroup.id); if (defaultXChannel == undefined && this.xChannelOptions[channelGroup.id].length > 0) { defaultXChannel = this.xChannelOptions[channelGroup.id][0]; @@ -415,7 +399,7 @@ * @param array the array * @param id the id */ - private isNodeIn(node: Node, array: Node[]) { + private isChannelIn(node: Channel, array: Channel[]) { let response = false; if (node != undefined && this.arrayUtil.isNotEmpty(array)) { response = array.findIndex(n => n.id === node.id) > -1; @@ -424,26 +408,29 @@ } /** - * Looks up parent channelGroup for channel via the channel map. + * Returns true if node with given id is in the given array. * - * @param channel + * @param array the array + * @param id the id */ - private findChannelGroup(channel: Node) { - const channelGroupId = Array.from(this.channels.keys()) - .find(key => this.isNodeIn(channel, this.channels.get(key))); - return this.channelGroups.find(cg => cg.id === channelGroupId); + private isChannelGroupIn(node: ChannelGroup, array: ChannelGroup[]) { + let response = false; + if (node != undefined && this.arrayUtil.isNotEmpty(array)) { + response = array.findIndex(n => n.id === node.id) > -1; + } + return response; } private fireSelectionChanged() { this.onSelectionChanged.emit(this.selectedChannelRows); } - private setSelectedYChannels(channels: Node[]) { + private setSelectedYChannels(channels: Channel[]) { this.selectedYChannels = channels; this.lastYChannelSelection = channels; } - private setSelectedChannelGroups(channelGroups: Node[]) { + private setSelectedChannelGroups(channelGroups: ChannelGroup[]) { this.selectedChannelGroups = channelGroups; this.lastChannelGroupSelection = channelGroups; }
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.html index 01b2142..3dd144b 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.html +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.html
@@ -12,12 +12,12 @@ * ********************************************************************************--> -<div *ngIf="measurement === undefined || channelGroups === undefined || channels === undefined"> +<div *ngIf="measurement === undefined"> <div class="alert alert-info" style="margin: 0;"> <strong>{{'chartviewer.xy-chart-viewer-nav-card.no-node-selected' | translate}}</strong> </div> </div> -<div *ngIf="measurement !== undefined && channelGroups !== undefined && channels !== undefined"> - <app-xy-chart-viewer [channelGroups]="channelGroups" [channels]="channels"></app-xy-chart-viewer> +<div *ngIf="measurement !== undefined"> + <app-xy-chart-viewer [measurement]="measurement"></app-xy-chart-viewer> </div> \ No newline at end of file
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.ts index af4d82d..ae565e8 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer-nav-card/xy-chart-viewer-nav-card.component.ts
@@ -19,9 +19,15 @@ import { TYPE_CHANNEL, TYPE_CHANNELGROUP, TYPE_MEASUREMENT } from '../../model/constants'; import { Subscription, forkJoin } from 'rxjs'; import { NodeService } from 'src/app/navigator/node.service'; -import { map, tap } from 'rxjs/operators'; +import { groupBy, map, tap } from 'rxjs/operators'; import { MDMItem } from 'src/app/core/mdm-item'; import { ChartViewerService } from '../../services/chart-viewer.service'; +import { ChannelGroup } from '../../model/types/channelgroup.class'; +import { Query, QueryService, Row } from 'src/app/tableview/query.service'; +import { Channel } from '../../model/types/channel.class'; +import { Measurement } from '../../model/types/measurement.class'; +import { nodeChildrenAsMap } from '@angular/router/src/utils/tree'; +import { ChartViewerDataService } from '../../services/chart-viewer-data.service'; @Component({ selector: 'app-xy-chart-viewer-nav-card', @@ -29,9 +35,7 @@ }) export class XyChartViewerNavCardComponent implements OnInit, OnDestroy { - public channels = new Map<string, Node[]>(); - public channelGroups: Node[]; - public measurement: Node; + public measurement: Measurement; private selectedNode: Node; @@ -42,7 +46,9 @@ private navigatorService: NavigatorService, private notificationService: MDMNotificationService, private nodeService: NodeService, - private chartViewerService: ChartViewerService) {} + private chartViewerService: ChartViewerService, + private chartViewerDataService: ChartViewerDataService, + private queryService: QueryService) {} ngOnInit() { this.selectedNodeChanged(this.navigatorService.getSelectedNode()); @@ -83,96 +89,55 @@ case TYPE_CHANNEL: this.selectedChannel(node); break; + default: + this.selectedNode = undefined; + this.measurement = undefined; + this.chartViewerService.sendNodeMeta(this.selectedNode); } } } // reload all, if channel is not present yet. - private selectedChannel(node: Node) { - if (!this.isChannelPresent(node)) { - this.loadMeasurement(node); + private selectedChannel(channel: Node) { + if (!this.isChannelPresent(channel)) { + this.loadMeasurement(TYPE_CHANNEL, channel); } else { this.chartViewerService.sendNodeMeta(this.selectedNode); } } - private selectedChannelGroup(node: Node) { - if (!this.isChannelGroupPresent(node)) { - this.loadMeasurement(node); + private selectedChannelGroup(channelGroup: Node) { + if (!this.isChannelGroupPresent(channelGroup)) { + this.loadMeasurement(TYPE_CHANNELGROUP, channelGroup); } else { this.chartViewerService.sendNodeMeta(this.selectedNode); } } - private selectedMeasurement(node: Node) { - if (this.measurement == undefined || this.measurement.id !== node.id) { - this.measurement = node; - this.loadChannelGroups(node); + private selectedMeasurement(measurement: Node) { + if (this.measurement == undefined || this.measurement.id !== measurement.id) { + this.loadMeasurement(TYPE_MEASUREMENT, measurement); } else { this.chartViewerService.sendNodeMeta(this.selectedNode); } } + private isChannelGroupPresent(node: Node) { + if (this.measurement) { + return this.measurement.findChannelGroup(node.id) !== undefined; + } + } + private isChannelPresent(node: Node) { - if (this.channels != undefined) { - /** - * @TODO more readable code, but got the impression that performance is bad. Read up on performance topic in depth. - */ - // Array.from(this.channels.values()) - // .reduce((a,b) => a.concat(b), []) - // .findIndex(c => c.id === node.id); - let index = -1; - const iterator = this.channels.values(); - let res = iterator.next(); - while (index === -1 && !res.done) { - index = res.value.findIndex(c => c.id === node.id); - res = iterator.next(); - } - return index > -1; + if (this.measurement) { + return this.measurement.findChannel(node.id) !== undefined; } - return false; } - private isChannelGroupPresent(node: Node) { - if (this.channelGroups != undefined) { - return this.channelGroups.findIndex(channelGroup => channelGroup.id === node.id) > -1; - } - return false; + private loadMeasurement(type: string, node: Node) { + this.chartViewerDataService.loadMeasurement(node).subscribe(mea => { + this.measurement = mea; + this.chartViewerService.sendNodeMeta(this.selectedNode); + }); } - - private loadMeasurement(node: Node) { - this.searchNodes(node, TYPE_MEASUREMENT) - .pipe( - /** @TODO in general true? */ - // parent measurement must be distinct - map(measurements => measurements[0]), - ).subscribe(measurement => { - this.measurement = measurement; - this.loadChannelGroups(this.measurement); - }); - } - - private loadChannelGroups(measurement: Node) { - this.searchNodes(measurement, TYPE_CHANNELGROUP) - .subscribe(channelGroups => { - this.channelGroups = channelGroups; - if (this.channelGroups != undefined) { - this.loadChannels(channelGroups); - } - }); - } - - private loadChannels(channelGroups: Node[]) { - this.channels.clear(); - let obsArray = channelGroups.map(channelGroup => this.searchNodes(channelGroup, TYPE_CHANNEL)); - forkJoin(obsArray).pipe( - tap(array => array.forEach((channels, index) => this.channels.set(channelGroups[index].id, channels))) - ).subscribe(datasets => this.chartViewerService.sendNodeMeta(this.selectedNode)); - } - - private searchNodes(node: Node, targetType: string) { - const filter = 'filter=' + node.type + '.Id eq \"' + node.id + '\"'; - return this.nodeService.searchNodes(filter, node.sourceName, targetType); - } - }
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.html index 9fa9fbe..ac12ce8 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.html +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.html
@@ -26,8 +26,7 @@ <!-- Axis selection --> <div [ngClass]="showSelection ? 'p-col-3' : 'hidden'"> <mdm5-xy-chart-data-selection-panel - [channelGroups]="channelGroups" - [channels]="channels" + [measurement]="measurement" [xyMode]="xyMode" (onSelectionChanged)="onDataSelectionChanged($event)" ></mdm5-xy-chart-data-selection-panel> @@ -36,14 +35,14 @@ <div [ngClass]="showSelection ? 'p-col-9' : 'p-col-12'"> <div [ngSwitch]="selectedMode"> <p-chart #xyChart *ngSwitchCase="'chart'" type="scatter" [data]="data"></p-chart> - <mdm5-dataTable *ngSwitchCase="'table'" [channelGroupMap]="channelGroupMap"></mdm5-dataTable> + <mdm5-dataTable *ngSwitchCase="'table'" [measurement]="measurement"></mdm5-dataTable> <div *ngSwitchDefault></div> </div> </div> </div> <div class="p-grid"> <div class="p-col-12"> - <mdm5-request-options [channelGroups]="channelGroups" (onRequestOptionsChanged)="onRequestOptionsChanged($event)"></mdm5-request-options> + <mdm5-request-options [measurement]="measurement" (onRequestOptionsChanged)="onRequestOptionsChanged($event)"></mdm5-request-options> </div> </div>
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.ts index 68447e6..0af5b88 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-viewer/xy-chart-viewer.component.ts
@@ -28,6 +28,8 @@ import { MDMNotificationService } from 'src/app/core/mdm-notification.service'; import { SelectItem } from 'primeng/api'; import { TranslateService } from '@ngx-translate/core'; +import { Measurement } from '../../model/types/measurement.class'; +import { Channel } from '../../model/types/channel.class'; @Component({ selector: 'app-xy-chart-viewer', @@ -40,11 +42,7 @@ public chart: UIChart; @Input() - public channelGroups: Node[]; - @Input() - public channels: Map<string, Node[]>; - // the channelgroup map for the data-table display - channelGroupMap: Map<Node, Node[]>; + public measurement: Measurement; // the chart data public data: ChartData; @@ -87,15 +85,7 @@ */ public onDataSelectionChanged(rows: ChannelSelectionRow[]) { this.selectedChannelRows = rows; - this.channelGroupMap = new Map(); - rows.forEach(row => { - let channels = this.channelGroupMap.get(row.channelGroup); - if (!channels) { - channels = []; - } - channels.push(row.yChannel); - this.channelGroupMap.set(row.channelGroup, channels); - }); + this.doChart(); } @@ -191,7 +181,7 @@ * Loads measured values for y-channels, merges them with given values for x * @param xValues */ - private loadYData(xValues: {yChannel: Node; measuredValues: MeasuredValues}[]) { + private loadYData(xValues: {yChannel: Channel; measuredValues: MeasuredValues}[]) { if (xValues != undefined) { const yChannels = this.groupYChannelsByChannelGroup(this.selectedChannelRows); // const channelGroups = this.selectedChannelGroups.filter(cg => this.arrayUtil.isNotEmpty(yChannels[cg.id])); @@ -214,7 +204,7 @@ } } - private extractXMeasuredValues(xValues: {yChannel: Node; measuredValues: MeasuredValues}[], channelId: string) { + private extractXMeasuredValues(xValues: {yChannel: Channel; measuredValues: MeasuredValues}[], channelId: string) { const val = xValues.find(v => v.yChannel.id === channelId); if (val != undefined ) { return val.measuredValues;
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel-selection-row.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel-selection-row.ts index 23f0072..055ca38 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel-selection-row.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel-selection-row.ts
@@ -12,13 +12,15 @@ * ********************************************************************************/ import {Node} from '../../../navigator/node'; +import { Channel } from './channel.class'; +import { ChannelGroup } from './channelgroup.class'; export class ChannelSelectionRow { - channelGroup: Node; - xChannel: Node; - yChannel: Node; + channelGroup: ChannelGroup; + xChannel: Channel; + yChannel: Channel; - constructor(channelGroup: Node, yChannel: Node, xChannel?: Node) { + constructor(channelGroup: ChannelGroup, yChannel: Channel, xChannel?: Channel) { this.channelGroup = channelGroup; this.yChannel = yChannel; this.xChannel = xChannel;
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/axis-info.class.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel.class.ts similarity index 66% rename from nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/axis-info.class.ts rename to nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel.class.ts index ba252be..1760da8 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/axis-info.class.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channel.class.ts
@@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation + * Copyright (c) 2015-2020 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -12,8 +12,18 @@ * ********************************************************************************/ -export class AxisInfo { +import { TYPE_CHANNEL } from '../constants'; + +export class Channel { + source: string; + type = TYPE_CHANNEL; + id: string; + name: string; + channelGroupId: string; + isIndependent: boolean; axisType: string; - independent: boolean; -} \ No newline at end of file + + constructor () { + } +}
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channelgroup.class.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channelgroup.class.ts new file mode 100644 index 0000000..bfe9aa8 --- /dev/null +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/channelgroup.class.ts
@@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2015-2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +import { TYPE_CHANNELGROUP } from '../constants'; + +export class ChannelGroup { + source: string; + type = TYPE_CHANNELGROUP; + id: string; + name: string; + numberOfRows: number; + + constructor (source: string, id: string, numberOfRows: number) { + this.source = source; + this.id = id; + this.numberOfRows = numberOfRows; + } +}
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/measurement.class.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/measurement.class.ts new file mode 100644 index 0000000..03dde20 --- /dev/null +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/model/types/measurement.class.ts
@@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2015-2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +import { TYPE_MEASUREMENT } from '../constants'; +import { Channel } from './channel.class'; +import { ChannelGroup } from './channelgroup.class'; + +export class Measurement { + + source: string; + type = TYPE_MEASUREMENT; + id: string; + name: string; + channelGroups: ChannelGroup[] = []; + channels: Map<string, Channel[]> = new Map(); + + constructor (m?: Measurement) { + this.source = m && m.source || ''; + this.type = m && m.type || ''; + this.id = m && m.id || ''; + this.name = m && m.name || ''; + } + + put(channelGroup: ChannelGroup, channel: Channel) { + const cg = this.findChannelGroup(channelGroup.id); + if (cg === undefined) { + this.channelGroups.push(channelGroup); + this.channels.set(channelGroup.id, [channel]); + } else { + this.channels.get(channelGroup.id).push(channel); + } + } + + getFirstChannelGroup() { + return this.channelGroups[0]; + } + + getIndependentChannel(channelGroup: ChannelGroup) { + let independentChannels = (this.findChannels(channelGroup) || []).filter(c => c.isIndependent); + if (independentChannels.length > 0) { + return independentChannels[0]; + } else { + return undefined; + } + } + + hasChannelGroup(channelGroupId: string) { + return this.findChannelGroup(channelGroupId) !== undefined; + } + + hasChannel(channelId: string): unknown { + return this.findChannel(channelId) !== undefined; + } + + findChannelGroup(channelGroupId: string) { + return this.channelGroups.find(cg => cg.id === channelGroupId); + } + + findChannelGroupByChannel(channel: Channel) { + for (let entry of Array.from(this.channels.entries())) { + let c = entry[1].find(chan => chan.id === channel.id); + if (c !== undefined) { + return this.findChannelGroup(entry[0]); + } + } + return undefined; + } + + findChannel(channelId: string) { + for (let entry of Array.from(this.channels.entries())) { + let c = entry[1].find(channel => channel.id === channelId); + if (c !== undefined) { + return c; + } + } + return undefined; + } + findChannels(channelGroup: ChannelGroup) { + return this.channels.get(channelGroup.id); + } + + allChannels() { + let channels: Channel[] = []; + + for (let entry of Array.from(this.channels.values())) { + channels.push(...entry); + } + return channels; + } +}
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/services/chart-viewer-data.service.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/services/chart-viewer-data.service.ts index ccd3732..7bf05df 100644 --- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/services/chart-viewer-data.service.ts +++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/services/chart-viewer-data.service.ts
@@ -16,15 +16,18 @@ import { Injectable } from '@angular/core'; import { plainToClass } from 'class-transformer'; -import { map, tap, catchError } from 'rxjs/operators'; +import { map, catchError } from 'rxjs/operators'; import { HttpErrorHandler } from '../../core/http-error-handler'; import { PropertyService } from '../../core/property.service'; import { Node } from '../../navigator/node'; import { MeasuredValuesResponse, MeasuredValues, PreviewValueList } from '../model/chartviewer.model'; import { QueryService, Query, SearchResult, Row } from '../../tableview/query.service'; -import { Observable } from 'rxjs'; -import { AxisInfo } from '../model/types/axis-info.class'; +import { throwError } from 'rxjs'; +import { ChannelGroup } from '../model/types/channelgroup.class'; +import { Channel } from '../model/types/channel.class'; +import { Measurement } from '../model/types/measurement.class'; +import { TYPE_CHANNEL, TYPE_CHANNELGROUP, TYPE_MEASUREMENT } from '../model/constants'; export function getDataArray(m: MeasuredValues) { if (m.scalarType === 'INTEGER') { @@ -73,7 +76,7 @@ * @param requestSize number of requested values * @param chunks preview chunks. If 0, preview is disabled and original values are loaded */ - loadValues(channelGroup: Node, channels: Node[], startIndex = 0, requestSize = 0, chunks = 0) { + loadValues(channelGroup: ChannelGroup, channels: Channel[], startIndex = 0, requestSize = 0, chunks = 0) { if (chunks > 0) { return this.loadPreviewValues(channelGroup, channels, chunks, startIndex, requestSize); } else { @@ -88,7 +91,7 @@ * @param startIndex start index to load the data * @param requestSize number of requested values */ - loadMeasuredValues(channelGroup: Node, channels: Node[], startIndex = 0, requestSize = 0) { + loadMeasuredValues(channelGroup: ChannelGroup, channels: Channel[], startIndex = 0, requestSize = 0) { let readRequest = { 'channelGroupId': channelGroup.id, 'channelIds': channels !== undefined ? channels.filter(channel => channel.type === 'Channel').map(channel => channel.id) : [], @@ -97,12 +100,11 @@ 'valuesMode': 'CALCULATED' }; - return this.http.post<MeasuredValuesResponse>(this._contextUrl + '/' + channelGroup.sourceName + '/values/read', + return this.http.post<MeasuredValuesResponse>(this._contextUrl + '/' + channelGroup.source + '/values/read', readRequest, this.httpOptions) .pipe( map(res => plainToClass(MeasuredValues, res.values)), map(measurementValues => measurementValues.sort((a, b) => a.name.localeCompare(b.name))), - // tap(r => console.log(r)), catchError(this.httpErrorHandler.handleError) ); } @@ -115,7 +117,7 @@ * @param startIndex start index to load the data * @param requestSize number of requested values */ - loadPreviewValues(channelGroup: Node, channels: Node[], chunks: number, startIndex = 0, requestSize = 0) { + loadPreviewValues(channelGroup: ChannelGroup, channels: Channel[], chunks: number, startIndex = 0, requestSize = 0) { let readRequest = { 'channelGroupId': channelGroup.id, 'channelIds': channels !== undefined ? channels.filter(channel => channel.type === 'Channel').map(channel => channel.id) : [], @@ -128,7 +130,7 @@ 'readRequest': readRequest }; - return this.http.post<PreviewValueList>(this._contextUrl + '/' + channelGroup.sourceName + '/values/preview', + return this.http.post<PreviewValueList>(this._contextUrl + '/' + channelGroup.source + '/values/preview', previewRequest, this.httpOptions) .pipe( map(res => plainToClass(MeasuredValues, res.avg)), @@ -136,39 +138,47 @@ ); } - loadAxisInformation(channelGroup: Node) : Observable<AxisInfo[]>{ + loadMeasurement(node: Node) { + if (node.type !== TYPE_MEASUREMENT && node.type !== TYPE_CHANNELGROUP && node.type !== TYPE_CHANNEL) { + return throwError('Node must be of type: ' + TYPE_MEASUREMENT + ' or ' + TYPE_CHANNELGROUP + ' or ' + TYPE_CHANNEL); + } const query = new Query(); - query.addFilter(channelGroup.sourceName, 'ChannelGroup.Id eq ' + channelGroup.id); - query.columns = ['Channel.Name', 'LocalColumn.axistype', 'LocalColumn.IndependentFlag']; + query.addFilter(node.sourceName, node.type + '.Id eq ' + node.id); + query.columns = ['Measurement.Id', 'ChannelGroup.Id', 'ChannelGroup.Name', 'ChannelGroup.SubMatrixNoRows', + 'Channel.Id', 'Channel.Name', 'LocalColumn.axistype', 'LocalColumn.IndependentFlag']; query.resultType = 'Channel'; + return this.queryService.query(query).pipe( - map(res => plainToClass(SearchResult, res)), - map(r => r.rows.map(row => { return { - name: row.getColumn('Channel.Name'), - axisType: row.getColumn('LocalColumn.axistype'), - independent: row.getColumn('LocalColumn.IndependentFlag') == '1' - }})), - map(ai => ai.sort((a, b) => a.name.localeCompare(b.name))), + map(sr => sr.rows.map(row => this.mapRow(row))), + map(pairs => { + let mea = new Measurement(); + mea.type = TYPE_MEASUREMENT; + + pairs.forEach(entry => { + mea.put(entry.channelGroup, entry.channel); + mea.source = entry.channelGroup.source; + mea.id = entry.meaId; + }); + + return mea; + }), ); } - getNumberOfRows(channelGroup: Node) { - let attr = channelGroup !== undefined && channelGroup.attributes !== undefined ? - channelGroup.attributes.find(a => a.name === 'SubMatrixNoRows') : undefined; - if (attr === undefined) { - // TODO - console.log('Could not find attribute SubMatrixNoRows on ChannelGroup with id ' + - (channelGroup !== undefined ? channelGroup.id : 'undefined')); - return; - } - return parseInt(<string> attr.value, 10); - } + private mapRow(row: Row) { + let channel = new Channel(); + channel.id = row.id; + channel.name = row.getColumn('Channel.Name'); + channel.channelGroupId = row.getColumn('ChannelGroup.SubMatrixNoRows'); + channel.axisType = row.getColumn('LocalColumn.axistype'), + channel.isIndependent = row.getColumn('LocalColumn.IndependentFlag') === '1'; - loadChannelsForGroup(channelGroup: Node) { - const query = new Query(); - query.addFilter(channelGroup.sourceName, 'ChannelGroup.Id eq ' + channelGroup.id); - query.columns = ['Channel.Id', 'Channel.Name', 'ChannelGroup.Id']; - query.resultType = 'Channel'; - return this.queryService.query(query); + let channelGroup = new ChannelGroup( + row.source, + row.getColumn('ChannelGroup.Id'), + parseInt(row.getColumn('ChannelGroup.SubMatrixNoRows'), 10)); + + channelGroup.name = row.getColumn('ChannelGroup.Name'); + return { meaId: row.getColumn('Measurement.Id'), channelGroup: channelGroup, channel: channel }; } }
diff --git a/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json b/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json index 21691a0..0917129 100644 --- a/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json +++ b/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json
@@ -440,7 +440,7 @@ "apply": "Übernehmen" }, "chart-viewer-nav-card": { - "load-missing-channels": "{{amount}} Kanäle nachladen" + "load-missing-channels": "{{limit}} Kanäle nachladen ({{offset}}/{{total}})" }, "xy-chart-data-selection-panel": { "select-channel-placeholder": "Kanal wählen",
diff --git a/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json b/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json index 125bf15..7f59191 100644 --- a/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json +++ b/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json
@@ -440,7 +440,7 @@ "apply": "Apply" }, "chart-viewer-nav-card": { - "load-missing-channels": "Load {{amount}} channels" + "load-missing-channels": "Load {{limit}} channels ({{offset}}/{{total}})" }, "xy-chart-data-selection-panel": { "select-channel-placeholder": "Select Channel",