| /******************************************************************************** |
| * Copyright (c) 2015-2018 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 { Component, ViewChild, Input, OnChanges, SimpleChanges } from '@angular/core'; |
| |
| import { LazyLoadEvent, Column } from 'primeng/primeng'; |
| import { Table } from 'primeng/table'; |
| |
| import { ChartViewerDataService } from '../../services/chart-viewer-data.service'; |
| import { ChannelSelectionRow, MeasuredValues } from '../../model/chartviewer.model'; |
| import { Measurement } from '../../model/types/measurement.class'; |
| import { getDataArray } from '../../model/types/measured-values.class'; |
| import { forkJoin, Observable, of } from 'rxjs'; |
| import { Channel } from '../../model/types/channel.class'; |
| import { map } from 'rxjs/operators'; |
| import { ChannelGroup } from '../../model/types/channelgroup.class'; |
| |
| @Component({ |
| selector: 'mdm5-dataTable', |
| templateUrl: 'data-table.component.html', |
| styleUrls: ['./data-table.component.css'], |
| providers: [] |
| }) |
| export class DataTableComponent implements OnChanges { |
| @ViewChild('datatable') |
| private table: Table; |
| |
| @Input() |
| measurement: Measurement; |
| @Input() |
| selectedChannelRows: ChannelSelectionRow[]; |
| |
| tableLoading = false; |
| cols: Column[] = []; |
| totalRecords = 0; |
| recordsPerPage = 10; |
| datapoints = []; |
| |
| constructor(private chartviewerService: ChartViewerDataService) { |
| } |
| |
| ngOnChanges(changes: SimpleChanges) { |
| for (const propName in changes) { |
| if (propName === 'measurement') { |
| this.table.reset(); |
| this.cols = []; |
| this.datapoints = []; |
| } |
| |
| if (propName === 'selectedChannelRows') { |
| this.table.reset(); |
| this.cols = []; |
| this.datapoints = []; |
| } |
| } |
| } |
| |
| load(mv: MeasuredValues[]) { |
| const zip = (rows => rows[0].map((_, c) => rows.map(row => row[c]))); |
| // combine the columns of multiple data requests |
| this.cols = this.cols.concat(mv.filter((m, i) => { |
| for (let j = 0; j < this.cols.length; j++) { |
| if (this.cols[j].header === m.name) { |
| return false; |
| } |
| } |
| return true; |
| }).map((m, i) => Object.assign(new Column(), { header: m.name, field: i }))); |
| return zip(mv.map(m => getDataArray(m).values)); |
| } |
| |
| loadLazy(event: LazyLoadEvent) { |
| if (!this.measurement) { |
| return; |
| } |
| |
| if (this.selectedChannelRows) { |
| const channelsByGroup = this.groupChannelRowsByChannelGroup(this.selectedChannelRows); |
| forkJoin( |
| of(this.selectedChannelRows.map(x => x.channelGroup.numberOfRows).reduce((prev, curr) => Math.max(prev, curr), 0)), |
| this.requestMeasuredValues(channelsByGroup, event.first, event.rows)) |
| .subscribe(res => { |
| this.totalRecords = res[0]; |
| this.datapoints = this.load(res[1].map(x => x.measuredValues)); |
| }); |
| } |
| } |
| |
| /** |
| * Group the ChannelSelectionRows by ChannelGroup, e.g. the returned record has the ID of the |
| * channelGroup as key and the list of corresponding Channels (X and Y Channels) as value. |
| * @param channelRows |
| */ |
| private groupChannelRowsByChannelGroup(channelRows: ChannelSelectionRow[]) { |
| return channelRows.reduce((previous, currentItem) => { |
| const group = currentItem.channelGroup.id; |
| |
| if (group != null) { |
| if (!previous[group]) { |
| previous[group] = { channelGroup: currentItem.channelGroup, channels: [] }; |
| } |
| if (currentItem.xChannel != undefined |
| && previous[group].channels.findIndex(c => c.id === currentItem.xChannel.id) == -1) { |
| previous[group].channels.push(currentItem.xChannel); |
| } |
| if (currentItem.yChannel != undefined |
| && previous[group].channels.findIndex(c => c.id === currentItem.yChannel.id) == -1) { |
| previous[group].channels.push(currentItem.yChannel); |
| } |
| } |
| |
| return previous; |
| }, {} as Record<string, {channelGroup: ChannelGroup, channels: Channel[]}>); |
| } |
| |
| /** |
| * Requests the values of all Channels for all ChannelGroups. Returns an Observable containing |
| * all Channels together with their MeasuredValues |
| * @param channelsByGroup |
| */ |
| private requestMeasuredValues(channelsByGroup: Record<string, {channelGroup: ChannelGroup, channels: Channel[]}>, startIndex: number, requestSize: number) { |
| let measuredValues : Observable<{ channel: Channel, measuredValues: MeasuredValues}[]>[] = [] |
| for (const channelGroupId in channelsByGroup) { |
| const channelGroup = this.selectedChannelRows.find(row => row.channelGroup.id == channelGroupId).channelGroup; |
| |
| let rowCount = channelsByGroup[channelGroupId].channelGroup.numberOfRows; |
| if (startIndex < rowCount) { |
| let obs = this.chartviewerService.loadValues(channelGroup, channelsByGroup[channelGroupId].channels, |
| startIndex, Math.min(requestSize, rowCount - startIndex), 0).pipe( |
| map(mv => this.mergeMeasuredValuesByName(channelsByGroup[channelGroupId].channels, mv)) |
| ) |
| |
| measuredValues.push(obs); |
| } |
| } |
| return forkJoin(measuredValues).pipe(map(mv => [].concat(...mv) as { channel: Channel, measuredValues: MeasuredValues}[])); |
| } |
| |
| /** |
| * Merge list of Channels with MeasuredValues by channel name. Only channels of one ChannelGroup |
| * are allowed, because names of Channels within a ChannelGroup are unique. |
| * @param channels |
| * @param measuredValues |
| */ |
| private mergeMeasuredValuesByName(channels: Channel[], measuredValues: MeasuredValues[]) { |
| return channels.map(c => { return { channel: c, measuredValues: measuredValues.find(mv => mv.name === c.name)}; }); |
| } |
| } |
| |