|
|
|
@ -16,12 +16,20 @@ |
|
|
|
|
|
|
|
|
|
|
|
import { WidgetContext } from '@home/models/widget-component.models'; |
|
|
|
import { deepClone, isDefined, isNumber, isUndefined } from '@app/core/utils'; |
|
|
|
import { IWidgetSubscription } from '@core/api/widget-api.models'; |
|
|
|
import { DatasourceData, JsonSettingsSchema } from '@app/shared/models/widget.models'; |
|
|
|
import { deepClone, isDefined, isEqual, isNumber, isUndefined } from '@app/core/utils'; |
|
|
|
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
|
|
|
import { |
|
|
|
DataKey, |
|
|
|
Datasource, |
|
|
|
DatasourceData, |
|
|
|
DatasourceType, |
|
|
|
JsonSettingsSchema, |
|
|
|
widgetType |
|
|
|
} from '@app/shared/models/widget.models'; |
|
|
|
import { |
|
|
|
ChartType, |
|
|
|
flotDatakeySettingsSchema, flotPieDatakeySettingsSchema, |
|
|
|
flotDatakeySettingsSchema, |
|
|
|
flotPieDatakeySettingsSchema, |
|
|
|
flotPieSettingsSchema, |
|
|
|
flotSettingsSchema, |
|
|
|
TbFlotAxisOptions, |
|
|
|
@ -33,6 +41,8 @@ import { |
|
|
|
TbFlotSeries, |
|
|
|
TbFlotSeriesHoverInfo, |
|
|
|
TbFlotSettings, |
|
|
|
TbFlotThresholdKeySettings, |
|
|
|
TbFlotThresholdMarking, |
|
|
|
TbFlotTicksFormatterFunction, |
|
|
|
TooltipValueFormatFunction |
|
|
|
} from './flot-widget.models'; |
|
|
|
@ -40,8 +50,9 @@ import * as moment_ from 'moment'; |
|
|
|
import * as tinycolor_ from 'tinycolor2'; |
|
|
|
import { AggregationType } from '@shared/models/time/time.models'; |
|
|
|
import { CancelAnimationFrame } from '@core/services/raf.service'; |
|
|
|
import Timeout = NodeJS.Timeout; |
|
|
|
import { UtilsService } from '@core/services/utils.service'; |
|
|
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|
|
|
import Timeout = NodeJS.Timeout; |
|
|
|
|
|
|
|
const tinycolor = tinycolor_; |
|
|
|
const moment = moment_; |
|
|
|
@ -51,12 +62,13 @@ const flotPieDatakeySettingsSchemaValue = flotPieDatakeySettingsSchema; |
|
|
|
|
|
|
|
export class TbFlot { |
|
|
|
|
|
|
|
private readonly utils: UtilsService; |
|
|
|
|
|
|
|
private settings: TbFlotSettings; |
|
|
|
|
|
|
|
private readonly tooltip: JQuery<any>; |
|
|
|
|
|
|
|
private readonly yAxisTickFormatter: TbFlotTicksFormatterFunction; |
|
|
|
private ticksFormatterFunction: TbFlotTicksFormatterFunction; |
|
|
|
private readonly yaxis: TbFlotAxisOptions; |
|
|
|
private readonly xaxis: TbFlotAxisOptions; |
|
|
|
private yaxes: Array<TbFlotAxisOptions>; |
|
|
|
@ -73,6 +85,9 @@ export class TbFlot { |
|
|
|
|
|
|
|
private readonly defaultBarWidth: number; |
|
|
|
|
|
|
|
private thresholdsSourcesSubscription: IWidgetSubscription; |
|
|
|
private predefinedThresholds: TbFlotThresholdMarking[]; |
|
|
|
|
|
|
|
private plotInited = false; |
|
|
|
private plot: JQueryPlot; |
|
|
|
|
|
|
|
@ -118,7 +133,7 @@ export class TbFlot { |
|
|
|
constructor(private ctx: WidgetContext, private readonly chartType: ChartType) { |
|
|
|
this.chartType = this.chartType || 'line'; |
|
|
|
this.settings = ctx.settings as TbFlotSettings; |
|
|
|
const utils = this.ctx.$injector.get(UtilsService); |
|
|
|
this.utils = this.ctx.$injector.get(UtilsService); |
|
|
|
this.tooltip = $('#flot-series-tooltip'); |
|
|
|
if (this.tooltip.length === 0) { |
|
|
|
this.tooltip = this.createTooltipElement(); |
|
|
|
@ -145,7 +160,8 @@ export class TbFlot { |
|
|
|
grid: { |
|
|
|
hoverable: true, |
|
|
|
mouseActiveRadius: 10, |
|
|
|
autoHighlight: this.tooltipIndividual === true |
|
|
|
autoHighlight: this.tooltipIndividual === true, |
|
|
|
markings: [] |
|
|
|
}, |
|
|
|
selection : { mode : ctx.isMobile ? null : 'x' }, |
|
|
|
legend : { |
|
|
|
@ -167,7 +183,7 @@ export class TbFlot { |
|
|
|
}; |
|
|
|
if (this.settings.xaxis) { |
|
|
|
this.xaxis.font.color = this.settings.xaxis.color || this.xaxis.font.color; |
|
|
|
this.xaxis.label = utils.customTranslation(this.settings.xaxis.title, this.settings.xaxis.title) || null; |
|
|
|
this.xaxis.label = this.utils.customTranslation(this.settings.xaxis.title, this.settings.xaxis.title) || null; |
|
|
|
this.xaxis.labelFont.color = this.xaxis.font.color; |
|
|
|
this.xaxis.labelFont.size = this.xaxis.font.size + 2; |
|
|
|
this.xaxis.labelFont.weight = 'bold'; |
|
|
|
@ -181,7 +197,7 @@ export class TbFlot { |
|
|
|
this.yaxis.font.color = this.settings.yaxis.color || this.yaxis.font.color; |
|
|
|
this.yaxis.min = isDefined(this.settings.yaxis.min) ? this.settings.yaxis.min : null; |
|
|
|
this.yaxis.max = isDefined(this.settings.yaxis.max) ? this.settings.yaxis.max : null; |
|
|
|
this.yaxis.label = utils.customTranslation(this.settings.yaxis.title, this.settings.yaxis.title) || null; |
|
|
|
this.yaxis.label = this.utils.customTranslation(this.settings.yaxis.title, this.settings.yaxis.title) || null; |
|
|
|
this.yaxis.labelFont.color = this.yaxis.font.color; |
|
|
|
this.yaxis.labelFont.size = this.yaxis.font.size + 2; |
|
|
|
this.yaxis.labelFont.weight = 'bold'; |
|
|
|
@ -244,7 +260,7 @@ export class TbFlot { |
|
|
|
return ''; |
|
|
|
}; |
|
|
|
} |
|
|
|
xaxis.label = utils.customTranslation(this.settings.xaxisSecond.title, this.settings.xaxisSecond.title) || null; |
|
|
|
xaxis.label = this.utils.customTranslation(this.settings.xaxisSecond.title, this.settings.xaxisSecond.title) || null; |
|
|
|
xaxis.position = this.settings.xaxisSecond.axisPosition; |
|
|
|
} |
|
|
|
xaxis.tickLength = 0; |
|
|
|
@ -270,6 +286,10 @@ export class TbFlot { |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.chartType === 'line' && isFinite(this.settings.thresholdsLineWidth)) { |
|
|
|
this.options.grid.markingsLineWidth = this.settings.thresholdsLineWidth; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.chartType === 'bar') { |
|
|
|
this.options.series.lines = { |
|
|
|
show: false, |
|
|
|
@ -279,7 +299,8 @@ export class TbFlot { |
|
|
|
this.options.series.bars = { |
|
|
|
show: true, |
|
|
|
lineWidth: 0, |
|
|
|
fill: 0.9 |
|
|
|
fill: 0.9, |
|
|
|
align: this.settings.barAlignment || 'left' |
|
|
|
}; |
|
|
|
this.defaultBarWidth = this.settings.defaultBarWidth || 600; |
|
|
|
} |
|
|
|
@ -346,6 +367,8 @@ export class TbFlot { |
|
|
|
const colors: string[] = []; |
|
|
|
this.yaxes = []; |
|
|
|
const yaxesMap: {[units: string]: TbFlotAxisOptions} = {}; |
|
|
|
const predefinedThresholds: TbFlotThresholdMarking[] = []; |
|
|
|
const thresholdsDatasources: Datasource[] = []; |
|
|
|
|
|
|
|
let tooltipValueFormatFunction: TooltipValueFormatFunction = null; |
|
|
|
if (this.settings.tooltipValueFormatter && this.settings.tooltipValueFormatter.length) { |
|
|
|
@ -441,8 +464,54 @@ export class TbFlot { |
|
|
|
series.yaxis = series.yaxisIndex + 1; |
|
|
|
yaxis.keysInfo[i] = {hidden: false}; |
|
|
|
yaxis.show = true; |
|
|
|
|
|
|
|
if (keySettings.thresholds && keySettings.thresholds.length) { |
|
|
|
for (const threshold of keySettings.thresholds) { |
|
|
|
if (threshold.thresholdValueSource === 'predefinedValue' && isFinite(threshold.thresholdValue)) { |
|
|
|
const colorIndex = this.subscription.data.length + predefinedThresholds.length; |
|
|
|
this.generateThreshold(predefinedThresholds, series.yaxis, threshold.lineWidth, |
|
|
|
threshold.color, colorIndex, threshold.thresholdValue); |
|
|
|
} else if (threshold.thresholdEntityAlias && threshold.thresholdAttribute) { |
|
|
|
const entityAliasId = this.ctx.aliasController.getEntityAliasId(threshold.thresholdEntityAlias); |
|
|
|
if (!entityAliasId) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
let datasource = thresholdsDatasources.filter((thresholdDatasource) => { |
|
|
|
return thresholdDatasource.entityAliasId === entityAliasId; |
|
|
|
})[0]; |
|
|
|
const dataKey: DataKey = { |
|
|
|
type: DataKeyType.attribute, |
|
|
|
name: threshold.thresholdAttribute, |
|
|
|
label: threshold.thresholdAttribute, |
|
|
|
settings: { |
|
|
|
yaxis: series.yaxis, |
|
|
|
lineWidth: threshold.lineWidth, |
|
|
|
color: threshold.color |
|
|
|
} as TbFlotThresholdKeySettings, |
|
|
|
_hash: Math.random() |
|
|
|
}; |
|
|
|
if (datasource) { |
|
|
|
datasource.dataKeys.push(dataKey); |
|
|
|
} else { |
|
|
|
datasource = { |
|
|
|
type: DatasourceType.entity, |
|
|
|
name: threshold.thresholdEntityAlias, |
|
|
|
aliasName: threshold.thresholdEntityAlias, |
|
|
|
entityAliasId, |
|
|
|
dataKeys: [ dataKey ] |
|
|
|
}; |
|
|
|
thresholdsDatasources.push(datasource); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.subscribeForThresholdsAttributes(thresholdsDatasources); |
|
|
|
this.options.grid.markings = predefinedThresholds; |
|
|
|
this.predefinedThresholds = predefinedThresholds; |
|
|
|
|
|
|
|
this.options.colors = colors; |
|
|
|
this.options.yaxes = deepClone(this.yaxes); |
|
|
|
if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { |
|
|
|
@ -714,6 +783,72 @@ export class TbFlot { |
|
|
|
return yaxis; |
|
|
|
} |
|
|
|
|
|
|
|
private subscribeForThresholdsAttributes(datasources: Datasource[]) { |
|
|
|
const thresholdsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { |
|
|
|
datasources, |
|
|
|
useDashboardTimewindow: false, |
|
|
|
type: widgetType.latest, |
|
|
|
callbacks: { |
|
|
|
onDataUpdated: (subscription) => {this.thresholdsSourcesDataUpdated(subscription.data)} |
|
|
|
} |
|
|
|
}; |
|
|
|
this.ctx.subscriptionApi.createSubscription(thresholdsSourcesSubscriptionOptions, true).subscribe( |
|
|
|
(subscription) => { |
|
|
|
this.thresholdsSourcesSubscription = subscription; |
|
|
|
} |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private thresholdsSourcesDataUpdated(data: DatasourceData[]) { |
|
|
|
const allThresholds = deepClone(this.predefinedThresholds); |
|
|
|
data.forEach((keyData) => { |
|
|
|
if (keyData && keyData.data && keyData.data[0]) { |
|
|
|
const attrValue = keyData.data[0][1]; |
|
|
|
if (isFinite(attrValue)) { |
|
|
|
const settings: TbFlotThresholdKeySettings = keyData.dataKey.settings; |
|
|
|
const colorIndex = this.subscription.data.length + allThresholds.length; |
|
|
|
this.generateThreshold(allThresholds, settings.yaxis, settings.lineWidth, settings.color, colorIndex, attrValue); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
this.options.grid.markings = allThresholds; |
|
|
|
this.redrawPlot(); |
|
|
|
} |
|
|
|
|
|
|
|
private generateThreshold(existingThresholds: TbFlotThresholdMarking[], yaxis: number, lineWidth: number, |
|
|
|
color: string, defaultColorIndex: number, thresholdValue: number) { |
|
|
|
const marking: TbFlotThresholdMarking = {}; |
|
|
|
let markingYAxis; |
|
|
|
|
|
|
|
if (yaxis !== 1) { |
|
|
|
markingYAxis = 'y' + yaxis + 'axis'; |
|
|
|
} else { |
|
|
|
markingYAxis = 'yaxis'; |
|
|
|
} |
|
|
|
|
|
|
|
if (isFinite(lineWidth)) { |
|
|
|
marking.lineWidth = lineWidth; |
|
|
|
} |
|
|
|
|
|
|
|
if (isDefined(color)) { |
|
|
|
marking.color = color; |
|
|
|
} else { |
|
|
|
marking.color = this.utils.getMaterialColor(defaultColorIndex); |
|
|
|
} |
|
|
|
|
|
|
|
marking[markingYAxis] = { |
|
|
|
from: thresholdValue, |
|
|
|
to: thresholdValue |
|
|
|
}; |
|
|
|
|
|
|
|
const similarMarkings = existingThresholds.filter((existingMarking) => { |
|
|
|
return isEqual(existingMarking[markingYAxis], marking[markingYAxis]); |
|
|
|
}); |
|
|
|
if (!similarMarkings.length) { |
|
|
|
existingThresholds.push(marking); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private seriesInfoDiv(label: string, color: string, value: any, |
|
|
|
units: string, trackDecimals: number, active: boolean, |
|
|
|
percent: number, valueFormatFunction: TooltipValueFormatFunction): JQuery<HTMLElement> { |
|
|
|
|