blob: 48dd6789b7d25ee3685aafc9e916ef8567f9b878 [file] [log] [blame]
/********************************************************************************
* 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, SimpleChanges, OnChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators';
import { UIChart } from 'primeng/primeng';
import { HttpErrorHandler } from '../../../core/http-error-handler';
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',
templateUrl: 'chart-viewer.component.html',
providers: []
})
export class ChartViewerComponent implements OnChanges {
@Input()
measurement: Measurement;
@ViewChild('lineChart')
chart: UIChart;
maxRequestedValues = 10000;
previewEnabled = true;
numberOfChunks = 600;
numberOfRows = 0;
startIndex = 1;
requestedValues = 0;
chartTimer: any;
// range of values to show; 1-based; start and end inclusive
rangeValues: number[] = [this.startIndex, this.requestedValues];
step = 1000;
data = this.getEmptyData();
tmpData = this.getEmptyData();
options = {
elements: {
point: {
radius: 0
},
line: {
tension: 0
}
},
legend: {
display: true,
},
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'xy'
},
zoom: {
enabled: true,
mode: 'xy'
}
}
},
};
constructor(private httpErrorHandler: HttpErrorHandler,
private chartService: ChartViewerDataService) {
}
// invoked from chart-viewer-nav-card.onSelectedNodeChange().subscribe calls
ngOnChanges(changes: SimpleChanges) {
this.numberOfRows = 0;
this.data = this.getEmptyData();
this.tmpData = this.getEmptyData();
for (const propName in changes) {
if (this.measurement !== undefined && (propName === 'measurement')) {
this.measurement.channelGroups.forEach(group => {
this.numberOfRows += group.numberOfRows;
if (this.numberOfRows < this.numberOfChunks) {
this.previewEnabled = false;
}
this.requestedValues = Math.min(this.numberOfRows, this.maxRequestedValues);
this.updateRange();
this.chartChannel(group);
});
break;
}
}
}
getEmptyData() {
return {
labels: [],
datasets: [
{
label: 'No data',
data: [],
borderColor: '#fff',
}
]
};
}
getColor(name: string) {
return this.data.datasets.find(dataset => dataset.label === name).borderColor;
}
applySettings(event: any) {
this.chartChannel(undefined);
}
chartChannel(channelGroup: ChannelGroup) {
let group: ChannelGroup;
if (channelGroup) {
group = channelGroup;
} else {
// either the selected channel or all channels from the map
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[]>) {
measuredValues.pipe(
map(mvl => <any>{
data: {
labels: this.getLabels(this.rangeValues[0], this.rangeValues[1], this.getLength(mvl)),
datasets: mvl.map(this.convertToDataset, this)
}
}),
catchError(this.httpErrorHandler.handleError)
).subscribe(d => {
if (this.tmpData.labels.length === 0) {
this.tmpData = d.data;
} else {
this.tmpData.datasets = this.tmpData.datasets.concat(d.data.datasets);
}
if (this.chartTimer) {
clearTimeout(this.chartTimer);
}
// adequate delay or the chart display will only render the latest group
this.chartTimer = setTimeout(() => this.processChartUpdate(), 250);
});
}
processChartUpdate() {
this.data = this.tmpData;
this.tmpData = this.getEmptyData();
}
updateRange() {
this.rangeValues = [this.startIndex, this.requestedValues];
this.step = Math.min(1000, Math.max(Math.floor(this.numberOfRows / 1000), 1));
}
getLabels(startIndex: number, endIndex: number, numberOfChunks: number) {
const labels: number[] = [];
for (let i = startIndex; i <= endIndex; i += (endIndex - startIndex + 1) / numberOfChunks) {
labels.push(Math.floor(i));
}
return labels;
}
getLength(measuredValues: MeasuredValues[]) {
return measuredValues.map(mv => mv.length).reduce((p, l) => Math.max(p, l));
}
convertToDataset(measuredValues: MeasuredValues) {
const data = getDataArray(measuredValues);
return {
label: measuredValues.name + ' [' + measuredValues.unit + ']',
unit: measuredValues.unit,
data: data ? data.values : [],
borderColor: '#' + Math.random().toString(16).substr(-6),
measuredValues: map,
channel: new Node()
};
}
}