import {
timer,
empty,
combineLatest,
of,
Subject,
Observable,
Subscription,
} from "rxjs";
import {
takeUntil,
debounceTime,
mergeMap,
concatMap,
distinctUntilChanged,
catchError,
} from "rxjs/operators";
import { Component, OnDestroy } from "@angular/core";
import { getOrDefault, WidgetConfig } from "../../widget.configuration";
import {
WidgetComponent,
WidgetId,
WidgetConfiguration,
WidgetConfigure,
WidgetInput,
WidgetOutput,
} from "../../widget.metadata";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import { TranslateService } from "@ngx-translate/core";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import * as uriTemplates_ from "uri-templates";
import { CurrentLocaleService } from "../../../components/i18n/currentLocale.service";
const uriTemplates = uriTemplates_;
export interface TimeSeriesChartConfiguration {
header?: string;
title?: string;
cssClass?: string;
showDataLabel?: boolean;
showXAxis?: boolean;
showYAxis?: boolean;
showXAxisLabel?: boolean;
showYAxisLabel?: boolean;
showLegend?: boolean;
colorScheme?: customColor[];
xAxisInterval?: number;
infoText?: string;
infoWidth?: string;
infoHeight?: string;
wikiLink?: string;
legendTitle?: boolean;
yAxisLabel?: string;
configurationColors?: customColor[];
handleError?: boolean;
chartInterval?: number;
}
@WidgetComponent("nm-time-series-chart")
@Component({
selector: "nm-time-series-chart",
templateUrl: "./time-series-chart.component.html",
styleUrls: ["./time-series-chart.component.scss"],
})
export class TimeSeriesChartWidgetComponent implements OnDestroy {
public results: any[];
public header: string;
public title: string;
public cssClass: string;
public showDataLabel: boolean;
public showXAxis: boolean;
public showYAxis: boolean;
public showXAxisLabel: boolean;
public showYAxisLabel: boolean;
public showLegend: boolean;
public xAxisTicks: any[];
public chartType: string;
public xAxisInterval: number;
public xAxisLabel: string;
public yAxisLabel: string;
public wikiLink: string;
public infotext: string;
public legendTitle: string = "";
public currentLocale: string;
public handleError: boolean;
public customColors: customColor[];
private configurationColors: customColor[];
public unitOfMeasurement: string;
public yAxisLabelsFormat = this.formatNumber.bind(this);
public chartInterval: number;
public tooltipShowX: boolean;
public tooltipShowY: boolean;
colorScheme = {
domain: ["#8093c7", "#abc378", "#e57e99", "#ffc699", "#A1B4BC"],
};
@WidgetConfiguration()
public configuration: WidgetConfig<TimeSeriesChartConfiguration>;
@WidgetInput()
public chartUri = new Subject<string>();
@WidgetInput()
public chartBody = new Subject<any>();
@WidgetInput()
public resetChart = new Subject<boolean>();
@WidgetInput()
public measurementUnit = new Subject<any>();
@WidgetInput()
public customXLabel = new Subject<string>();
@WidgetInput()
public selectedUiLocale = new Subject<any>();
@WidgetOutput()
public errorHandler = new Subject<string>();
@WidgetId()
public widgetId: string;
private unsubscribe = NgUnsubscribe.create();
constructor(
private widgetframeService: WidgetframeService,
public translateService: TranslateService,
private currentLocaleService: CurrentLocaleService
) {}
@WidgetConfigure()
protected configureWidget(configuration: WidgetConfig) {
this.header = configuration.configuration.header;
this.title = configuration.configuration.title;
this.cssClass = configuration.configuration.cssClass || "";
this.showDataLabel = configuration.configuration.showDataLabel;
this.showXAxis = configuration.configuration.showXAxis;
this.showYAxis = configuration.configuration.showYAxis;
this.showXAxisLabel = configuration.configuration.showXAxisLabel;
this.showYAxisLabel = configuration.configuration.showYAxisLabel;
this.showLegend = configuration.configuration.showLegend;
this.colorScheme = configuration.configuration.customColors
? configuration.configuration.customColors
: this.colorScheme;
this.xAxisInterval = configuration.configuration.xAxisInterval;
this.infotext = configuration.configuration.infoText;
this.wikiLink = configuration.configuration.wikiLink;
this.legendTitle = configuration.configuration.legendTitle || "";
this.yAxisLabel = configuration.configuration.yAxisLabel || "";
this.configurationColors = configuration.configuration.customColorScheme;
this.handleError = configuration.configuration.handleError;
this.chartInterval = configuration.configuration.chartInterval || 1000;
this.tooltipShowX = getOrDefault(
configuration.configuration.tooltipShowX,
true
);
this.tooltipShowY = getOrDefault(
configuration.configuration.tooltipShowY,
false
);
this.resetChart
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((resetChart) => {
if (resetChart) {
this.results = [];
this.xAxisTicks = [];
}
});
combineLatest(
timer(this.chartInterval),
this.chartUri.asObservable(),
this.chartBody.asObservable(),
(interval, uri, body) => [uri, body]
)
.pipe(takeUntil(this.unsubscribe))
.pipe(
debounceTime(5),
mergeMap((input) => {
let url = input[0];
let body = input[1];
return this.widgetframeService.putData(url, body);
}),
catchError((error) => {
this.handleException(error);
return empty();
})
)
.subscribe((data) => {
if (data.message) {
this.handleException(data.message);
} else {
this.populateChart(data);
}
});
combineLatest(
this.measurementUnit.asObservable(),
this.currentLocaleService.getCurrentLocale()
)
.pipe(takeUntil(this.unsubscribe))
.subscribe(([data, locale]) => {
this.currentLocale = locale.toString();
if (data) {
this.yAxisLabel = data.unit;
this.unitOfMeasurement = this.yAxisLabel;
}
});
combineLatest(
this.customXLabel.asObservable(),
this.selectedUiLocale.asObservable()
)
.pipe(takeUntil(this.unsubscribe))
.pipe(
debounceTime(5),
mergeMap(([customXLabel, selectedUiLocale]) =>
this.translateService.get(customXLabel)
)
)
.subscribe((label) => {
this.xAxisLabel = label;
});
}
populateChart(data) {
this.unitOfMeasurement = data.unit;
this.yAxisLabel = data.unit;
this.chartType = data.chartType;
if (this.configurationColors) {
this.setCustomColors(data.values);
}
if (this.xAxisInterval) {
this.xAxisTicks = [];
if (data.values[0].hasOwnProperty("series")) {
this.setMultipleSeriesChartTicks(data.values);
} else {
this.setSingleSeriesChartTicks(data.values);
}
} else {
this.results = data.values;
this.xAxisTicks = data.xAxisTicks;
}
}
setSingleSeriesChartTicks(values) {
let resultsLength = values.length;
let breakpoint = Math.floor(resultsLength / this.xAxisInterval);
for (let x = 0; x < resultsLength; x += breakpoint) {
this.xAxisTicks.push(values[x].name);
}
this.results = values;
}
setMultipleSeriesChartTicks(values) {
let maxSeriesLength = 0;
let maxSeriesIndex = 0;
let breakpoint;
values.forEach((lineSeries, index) => {
let resultsLength = lineSeries.series.length;
if (resultsLength > maxSeriesLength) {
maxSeriesLength = resultsLength;
maxSeriesIndex = index;
}
});
breakpoint = Math.floor(maxSeriesLength / this.xAxisInterval);
this.xAxisTicks = [];
for (let x = 0; x < maxSeriesLength; x += breakpoint) {
this.xAxisTicks.push(values[maxSeriesIndex].series[x].name);
}
this.results = values;
}
setCustomColors(results: any[]) {
this.customColors = [];
results.forEach((value) => {
let customColor = this.configurationColors.find(
(color) => color.name === value.seriesColorIdentifier
);
if (customColor) {
this.customColors.push({
name: value.name,
value: customColor.value,
});
}
});
}
formatNumber(number) {
return number.toLocaleString(this.currentLocale);
}
handleException(message: string) {
this.results = [];
if (this.handleError) {
this.errorHandler.next(message);
}
}
ngOnDestroy(): void {
if (this.unsubscribe) {
this.unsubscribe.destroy();
}
}
}
export interface customColor {
name: string;
value: string;
}