diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts index b344e4b044..a91511ff13 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -33,7 +33,7 @@ import { import { formatValue, isUndefined } from '@core/utils'; import { Component } from '@angular/core'; import { - backwardCompatibilityFixedLevelColors, + convertLevelColorsSettingsToColorProcessor, defaultDigitalSimpleGaugeOptions, digitalGaugeLayoutImages, digitalGaugeLayouts, @@ -41,7 +41,7 @@ import { DigitalGaugeSettings, DigitalGaugeType } from '@home/components/widget/lib/digital-gauge.models'; -import { ColorSettings, ColorType, constantColor } from '@shared/models/widget-settings.models'; +import { ColorSettings, ColorType } from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-digital-simple-gauge-basic-config', @@ -98,28 +98,7 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom protected onConfigSet(configData: WidgetConfigComponentData) { const settings: DigitalGaugeSettings = {...defaultDigitalSimpleGaugeOptions, ...(configData.config.settings || {})}; - if (!settings.barColor) { - settings.barColor = constantColor(settings.gaugeColor); - if (settings.fixedLevelColors?.length) { - settings.barColor.rangeList = { - advancedMode: settings.useFixedLevelColor, - range: null, - rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) - }; - } - if (settings.levelColors?.length) { - settings.barColor.gradient = { - advancedMode: false, - gradient: settings.levelColors as string[], - gradientAdvanced: null - }; - } - if (settings.useFixedLevelColor) { - settings.barColor.type = ColorType.range; - } else if (settings.levelColors.length) { - settings.barColor.type = ColorType.gradient; - } - } + convertLevelColorsSettingsToColorProcessor(settings); this.simpleGaugeWidgetConfigForm = this.fb.group({ timewindowConfig: [getTimewindowConfig(configData.config), []], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts index dd875834f7..0dc41fd88b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts @@ -17,33 +17,19 @@ import * as CanvasGauges from 'canvas-gauges'; import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models'; import tinycolor from 'tinycolor2'; -import { ColorFormats } from 'tinycolor2'; -import { isDefined, isDefinedAndNotNull, isString, isUndefined, padValue } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, isUndefined, padValue } from '@core/utils'; +import { ColorProcessor, constantColor } from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; import BaseGauge = CanvasGauges.BaseGauge; export type GaugeType = 'arc' | 'donut' | 'horizontalBar' | 'verticalBar'; -export interface DigitalGaugeColorRange { - pct: number; - color: ColorFormats.RGBA; - rgbString: string; -} - -export interface ColorLevelSetting { - value: number; - color: string; -} - -export type levelColors = Array; - export interface CanvasDigitalGaugeOptions extends GenericOptions { gaugeType?: GaugeType; gaugeWithScale?: number; dashThickness?: number; roundedLineCap?: boolean; gaugeColor?: string; - levelColors?: levelColors; symbol?: string; hideValue?: boolean; hideMinMax?: boolean; @@ -65,8 +51,6 @@ export interface CanvasDigitalGaugeOptions extends GenericOptions { donutStartAngle?: number; donutEndAngle?: number; - colorsRange?: DigitalGaugeColorRange[]; - neonColorsRange?: DigitalGaugeColorRange[]; neonColorTitle?: string; neonColorLabel?: string; neonColorValue?: string; @@ -83,10 +67,12 @@ export interface CanvasDigitalGaugeOptions extends GenericOptions { colorTicks?: string; tickWidth?: number; - labelTimestamp?: string + labelTimestamp?: string; unitTitle?: string; showUnitTitle?: boolean; showTimestamp?: boolean; + + barColorProcessor: ColorProcessor; } const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOptions, @@ -97,7 +83,7 @@ const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOption roundedLineCap: false, gaugeColor: '#777', - levelColors: ['blue'], + barColorProcessor: ColorProcessor.fromSettings(constantColor('blue')), symbol: '', hideValue: false, @@ -189,7 +175,7 @@ export class Drawings { options['font' + target + 'Size'] * baseSize + 'px ' + options['font' + target]; } - static normalizedValue(options: CanvasGauges.GenericOptions): {normal: number, indented: number} { + static normalizedValue(options: CanvasGauges.GenericOptions): {normal: number; indented: number} { const value = options.value; const min = options.minValue; const max = options.maxValue; @@ -244,38 +230,38 @@ export class CanvasDigitalGauge extends BaseGauge { } } - const colorsCount = options.levelColors.length; - const isColorProperty = isString(options.levelColors[0]); - const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; - options.colorsRange = []; - if (options.neonGlowBrightness) { - options.neonColorsRange = []; - } - for (let i = 0; i < options.levelColors.length; i++) { - const levelColor: any = options.levelColors[i]; - if (levelColor !== null) { - let percentage: number; - if (isColorProperty) { - percentage = inc * i; - } else { - percentage = CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue); - } - let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color); - options.colorsRange.push({ - pct: percentage, - color: tColor.toRgb(), - rgbString: tColor.toRgbString() - }); - if (options.neonGlowBrightness) { - tColor = tinycolor(isColorProperty ? levelColor : levelColor.color).brighten(options.neonGlowBrightness); - options.neonColorsRange.push({ - pct: percentage, - color: tColor.toRgb(), - rgbString: tColor.toRgbString() - }); - } - } - } + // const colorsCount = options.levelColors.length; + // const isColorProperty = isString(options.levelColors[0]); + // const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; + // options.colorsRange = []; + // if (options.neonGlowBrightness) { + // options.neonColorsRange = []; + // } + // for (let i = 0; i < options.levelColors.length; i++) { + // const levelColor: any = options.levelColors[i]; + // if (levelColor !== null) { + // let percentage: number; + // if (isColorProperty) { + // percentage = inc * i; + // } else { + // percentage = CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue); + // } + // let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color); + // options.colorsRange.push({ + // pct: percentage, + // color: tColor.toRgb(), + // rgbString: tColor.toRgbString() + // }); + // if (options.neonGlowBrightness) { + // tColor = tinycolor(isColorProperty ? levelColor : levelColor.color).brighten(options.neonGlowBrightness); + // options.neonColorsRange.push({ + // pct: percentage, + // color: tColor.toRgb(), + // rgbString: tColor.toRgbString() + // }); + // } + // } + // } options.ticksValue = []; for (const tick of options.ticks) { if (tick !== null) { @@ -456,12 +442,12 @@ export class CanvasDigitalGauge extends BaseGauge { let color = this.contextProgressClone.currentColor; const options = this.options as CanvasDigitalGaugeOptions; if (!color) { - const progress = (Drawings.normalizedValue(options).normal - options.minValue) / - (options.maxValue - options.minValue); + options.barColorProcessor.update(options.value); + const calculateColor = tinycolor(options.barColorProcessor.color); if (options.neonGlowBrightness) { - color = getProgressColor(progress, options.neonColorsRange); + color = calculateColor.brighten(options.neonGlowBrightness).toRgbString(); } else { - color = getProgressColor(progress, options.colorsRange); + color = calculateColor.toRgbString(); } } return color; @@ -569,7 +555,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, const labelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor).height; const total = valueHeight + labelHeight; bd.labelY = bd.Cy + total / 2; - bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor) + bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor); bd.valueY = bd.Cy - total / 2 + valueHeight / 2; } else { bd.valueY = bd.Cy; @@ -578,7 +564,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, bd.titleY = bd.Cy - bd.Ro - 12 * bd.fontSizeFactor; bd.valueY = bd.Cy; bd.labelY = bd.Cy + (8 + options.fontLabelSize) * bd.fontSizeFactor; - bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor) + bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor); bd.minY = bd.maxY = bd.labelY; if (options.roundedLineCap) { bd.minY += bd.strokeWidth / 2; @@ -599,7 +585,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, if (options.hideMinMax && !options.showUnitTitle && !options.showTimestamp) { bd.labelY = bd.barBottom; - bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor) + bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor); bd.barLeft = bd.origBaseX + options.fontMinMaxSize / 3 * bd.fontSizeFactor; bd.barRight = bd.origBaseX + w + /*bd.width*/ -options.fontMinMaxSize / 3 * bd.fontSizeFactor; } else { @@ -612,7 +598,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, bd.barLeft = bd.minX; bd.barRight = bd.maxX; bd.labelY = bd.barBottom + (8 + options.fontLabelSize) * bd.fontSizeFactor; - bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor) + bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor); bd.minY = bd.maxY = bd.labelY; } } else if (options.gaugeType === 'verticalBar') { @@ -623,11 +609,11 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, bd.barTop = bd.valueY + 8 * bd.fontSizeFactor; bd.labelY = bd.baseY + bd.height; - bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor) + bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor); if (options.showUnitTitle || options.showTimestamp) { bd.barBottom = bd.labelY - options.fontLabelSize * bd.fontSizeFactor; } else { - bd.barBottom = bd.labelY + bd.barBottom = bd.labelY; } bd.minX = bd.maxX = bd.baseX + bd.width / 2 + bd.strokeWidth / 2 + options.fontMinMaxSize / 3 * bd.fontSizeFactor; bd.minY = bd.barBottom; @@ -637,7 +623,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, } if (options.dashThickness) { - let circumference; + let circumference: number; if (options.gaugeType === 'donut') { circumference = Math.PI * bd.Rm * 2; } else if (options.gaugeType === 'arc') { @@ -763,7 +749,10 @@ function drawDigitalTitle(context: DigitalGaugeCanvasRenderingContext2D, options context.restore(); } -function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, text: string, nameTextY: string) { +function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, + options: CanvasDigitalGaugeOptions, + text: string, + nameTextY: string) { if (!text || text === '') { return; } @@ -823,31 +812,6 @@ function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options context.restore(); } -function getProgressColor(progress: number, colorsRange: DigitalGaugeColorRange[]): string { - - if (progress === 0 || colorsRange.length === 1) { - return colorsRange[0].rgbString; - } - - for (let j = 1; j < colorsRange.length; j++) { - if (progress <= colorsRange[j].pct) { - const lower = colorsRange[j - 1]; - const upper = colorsRange[j]; - const range = upper.pct - lower.pct; - const rangePct = (progress - lower.pct) / range; - const pctLower = 1 - rangePct; - const pctUpper = rangePct; - const color = tinycolor({ - r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper), - g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper), - b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper) - }); - return color.toRgbString(); - } - } - return colorsRange[colorsRange.length - 1].rgbString; -} - function drawArcGlow(context: DigitalGaugeCanvasRenderingContext2D, Cx: number, Cy: number, Ri: number, Rm: number, Ro: number, color: string, progress: number, isDonut: boolean, @@ -953,12 +917,14 @@ function drawTickBar(context: DigitalGaugeCanvasRenderingContext2D, tickValues: function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, progress: number) { - let neonColor; + let neonColor: string; context.save(); + options.barColorProcessor.update(options.value); + const color = tinycolor(options.barColorProcessor.color); if (options.neonGlowBrightness) { - context.currentColor = neonColor = getProgressColor(progress, options.neonColorsRange); + context.currentColor = neonColor = color.brighten(options.neonGlowBrightness).toRgbString(); } else { - context.currentColor = context.strokeStyle = getProgressColor(progress, options.colorsRange); + context.currentColor = context.strokeStyle = color.toRgbString(); } const {barLeft, barRight, barTop, baseX, width, barBottom, Cx, Cy, Rm, Ro, Ri, strokeWidth} = diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index 4f896ff2d3..76a3f84899 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -27,8 +27,8 @@ import { DateFormatSettings, Font, tsToFormatTimeUnit, - ValueSourceType, - ValueSourceTypeConfig + ValueSourceConfig, + ValueSourceType } from '@shared/models/widget-settings.models'; import { CallbackDataParams, @@ -722,7 +722,7 @@ export const defaultTimeSeriesChartXAxisSettings: TimeSeriesChartXAxisSettings = export type TimeSeriesChartYAxes = {[id: TimeSeriesChartYAxisId]: TimeSeriesChartYAxisSettings}; -export interface TimeSeriesChartThreshold extends ValueSourceTypeConfig { +export interface TimeSeriesChartThreshold extends ValueSourceConfig { yAxisId: TimeSeriesChartYAxisId; units?: string; decimals?: number; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 354761fa99..913c346712 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -471,8 +471,8 @@ export class TbTimeSeriesChart { entityAliasId, dataKeys: [entityDataKey] }; + thresholdDatasources.push(datasource); } - thresholdDatasources.push(datasource); } else { // constant value = threshold.value; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts index 5c80a820d1..886876041f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts @@ -20,8 +20,10 @@ import { FontSettings } from '@home/components/widget/lib/settings.models'; import { AdvancedColorRange, ColorSettings, - ValueSourceType, - ValueSourceTypeConfig + ColorType, + constantColor, + ValueSourceConfig, + ValueSourceType } from '@shared/models/widget-settings.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { isDefinedAndNotNull } from '@core/utils'; @@ -34,8 +36,8 @@ export interface AttributeSourceProperty { } export interface FixedLevelColors { - from?: ValueSourceTypeConfig | number; - to?: ValueSourceTypeConfig | number; + from?: AttributeSourceProperty; + to?: AttributeSourceProperty; color: string; } @@ -46,8 +48,6 @@ export interface ColorLevelSetting { export type colorLevel = Array; -export type attributesGaugeType = 'levelColors' | 'ticks'; - export enum DigitalGaugeType { arc = 'arc', donut = 'donut', @@ -112,7 +112,7 @@ export interface DigitalGaugeSettings { hideValue?: boolean; hideMinMax?: boolean; showTicks?: boolean; - ticksValue?: ValueSourceTypeConfig[]; + ticksValue?: ValueSourceConfig[]; ticks?: number[]; colorTicks?: string; tickWidth?: number; @@ -123,7 +123,7 @@ export const defaultDigitalSimpleGaugeOptions: DigitalGaugeSettings = { timestampFormat: 'yyyy-MM-dd HH:mm:ss', }; -export const backwardCompatibilityFixedLevelColors = (fixedLevelColors) => { +export const backwardCompatibilityFixedLevelColors = (fixedLevelColors: FixedLevelColors[]) => { const valueSourceWithDataKey: AdvancedColorRange[] = []; fixedLevelColors.forEach(fixedLevelColor => valueSourceWithDataKey.push({ from: { @@ -145,8 +145,8 @@ export const backwardCompatibilityFixedLevelColors = (fixedLevelColors) => { return valueSourceWithDataKey; }; -export const backwardCompatibilityTicks = (ticksValue) => { - const ticks: ValueSourceTypeConfig[] = []; +export const backwardCompatibilityTicks = (ticksValue: AttributeSourceProperty[] & ValueSourceConfig[]): ValueSourceConfig[] => { + const ticks: ValueSourceConfig[] = []; if (ticksValue?.length && isDefinedAndNotNull(ticksValue[0]?.valueSource)) { ticksValue.forEach(tick => ticks.push({ type: tick?.valueSource === 'predefinedValue' ? ValueSourceType.constant : ValueSourceType.entity, @@ -156,7 +156,32 @@ export const backwardCompatibilityTicks = (ticksValue) => { entityKeyType: DataKeyType.attribute }) ); } else { - return ticksValue; + return (ticksValue as ValueSourceConfig[]); } return ticks; }; + +export const convertLevelColorsSettingsToColorProcessor = (settings: DigitalGaugeSettings) => { + if (!settings.barColor) { + settings.barColor = constantColor(settings.gaugeColor); + if (settings.fixedLevelColors?.length) { + settings.barColor.rangeList = { + advancedMode: settings.useFixedLevelColor, + range: null, + rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) + }; + } + if (settings.levelColors?.length) { + settings.barColor.gradient = { + advancedMode: false, + gradient: settings.levelColors as string[], + gradientAdvanced: null + }; + } + if (settings.useFixedLevelColor) { + settings.barColor.type = ColorType.range; + } else if (settings.levelColors.length) { + settings.barColor.type = ColorType.gradient; + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index b88106ee1b..7650583a91 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -17,25 +17,23 @@ import * as CanvasGauges from 'canvas-gauges'; import { WidgetContext } from '@home/models/widget-component.models'; import { - attributesGaugeType, - ColorLevelSetting, - DigitalGaugeSettings, - FixedLevelColors + convertLevelColorsSettingsToColorProcessor, + DigitalGaugeSettings } from '@home/components/widget/lib/digital-gauge.models'; import tinycolor from 'tinycolor2'; -import { isDefined, isDefinedAndNotNull, parseFunction, safeExecute } from '@core/utils'; +import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import { DatePipe } from '@angular/common'; -import { DataKey, Datasource, DatasourceData, DatasourceType, widgetType } from '@shared/models/widget.models'; -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { EMPTY, Observable } from 'rxjs'; +import { Datasource } from '@shared/models/widget.models'; +import { IWidgetSubscription } from '@core/api/widget-api.models'; +import { Subscription } from 'rxjs'; import { ColorProcessor, - ColorType, - ValueSourceType, - ValueSourceTypeConfig + generateDatasource, + subscribeForDatasource, + ValueSourceConfig, + ValueSourceType } from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; @@ -68,19 +66,9 @@ export class TbCanvasDigitalGauge { this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); - this.localSettings.barColor = settings.barColor; - this.localSettings.useFixedLevelColor = settings.useFixedLevelColor || false; - if (!settings.useFixedLevelColor) { - if (!settings.levelColors || settings.levelColors.length === 0) { - this.localSettings.levelColors = [keyColor]; - } else { - this.localSettings.levelColors = settings.levelColors.slice(); - } - } else { - this.localSettings.levelColors = [keyColor]; - this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; - } + convertLevelColorsSettingsToColorProcessor(settings); + this.localSettings.barColor = settings.barColor; this.localSettings.showTicks = settings.showTicks || false; this.localSettings.ticks = []; @@ -135,12 +123,15 @@ export class TbCanvasDigitalGauge { color: keyColor }); + this.barColorProcessor = ColorProcessor.fromSettings(settings.barColor, this.ctx); + const gaugeData: CanvasDigitalGaugeOptions = { renderTo: gaugeElement, gaugeWidthScale: this.localSettings.gaugeWidthScale, gaugeColor: this.localSettings.gaugeColor, - levelColors: this.localSettings.levelColors, + + barColorProcessor: this.barColorProcessor, colorTicks: this.localSettings.colorTicks, tickWidth: this.localSettings.tickWidth, @@ -204,53 +195,12 @@ export class TbCanvasDigitalGauge { } private localSettings: DigitalGaugeSettings; - private levelColorsSourcesSubscription: IWidgetSubscription; private ticksSourcesSubscription: IWidgetSubscription; - private gauge: CanvasDigitalGauge; - - static generateDatasource(ctx: WidgetContext, datasources: Datasource[], entityAlias: string, - attribute: string, settings: any): Datasource[]{ - const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); - if (!entityAliasId) { - throw new Error('Not valid entity aliase name ' + entityAlias); - } - - const datasource = datasources.find((datasourceIteration) => - datasourceIteration.entityAliasId === entityAliasId - ); - - const dataKey: DataKey = { - type: DataKeyType.attribute, - name: attribute, - label: attribute, - settings: [settings], - _hash: Math.random() - }; + private readonly barColorProcessor: ColorProcessor; + private updatedBarColor$: Subscription; - if (datasource) { - const findDataKey = datasource.dataKeys.find((dataKeyIteration) => - dataKeyIteration.name === attribute - ); - - if (findDataKey) { - findDataKey.settings.push(settings); - } else { - datasource.dataKeys.push(dataKey); - } - } else { - const datasourceAttribute: Datasource = { - type: DatasourceType.entity, - name: entityAlias, - aliasName: entityAlias, - entityAliasId, - dataKeys: [dataKey] - }; - datasources.push(datasourceAttribute); - } - - return datasources; - } + private gauge: CanvasDigitalGauge; private static toRadians(angle: number): number { return angle * (Math.PI / 180); @@ -258,10 +208,6 @@ export class TbCanvasDigitalGauge { init() { let updateSetting = false; - if (this.localSettings.useFixedLevelColor && this.localSettings.fixedLevelColors?.length > 0) { - this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); - updateSetting = true; - } if (this.localSettings.showTicks && this.localSettings.ticksValue?.length) { this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue); updateSetting = true; @@ -270,68 +216,26 @@ export class TbCanvasDigitalGauge { if (updateSetting) { this.updateSetting(); } - } - - settingLevelColorsSubscribe(options: FixedLevelColors[]): ColorLevelSetting[] { - let levelColorsDatasource: Datasource[] = []; - const predefineLevelColors: ColorLevelSetting[] = []; - - predefineLevelColors.push({ - value: this.localSettings.minValue, - color: this.localSettings.gaugeColor - }); - - function setLevelColor(levelSetting: ValueSourceTypeConfig | number, color: string) { - if (typeof levelSetting === 'number') { - predefineLevelColors.push({ - value: levelSetting, - color - }); - } else if (levelSetting.type === ValueSourceType.constant && isFinite(levelSetting.value)) { - predefineLevelColors.push({ - value: levelSetting.value, - color - }); - } else if (levelSetting.type === ValueSourceType.latestKey || levelSetting.type === ValueSourceType.entity) { - try { - levelColorsDatasource = ColorProcessor.generateDatasource( - this.ctx, levelColorsDatasource, levelSetting, {color, index: predefineLevelColors.length} - ); - } catch (e) { - return; - } - predefineLevelColors.push(null); - } - } - for (const levelColor of options) { - if (levelColor.from) { - setLevelColor.call(this, levelColor.from, levelColor.color); - } - if (levelColor.to) { - setLevelColor.call(this, levelColor.to, levelColor.color); - } - } - - this.subscribeAttributes(levelColorsDatasource, 'levelColors').subscribe((subscription) => { - this.levelColorsSourcesSubscription = subscription; + this.updatedBarColor$ = this.barColorProcessor.colorUpdated.subscribe(() => { + this.gauge.update({} as CanvasDigitalGaugeOptions); }); - - return predefineLevelColors; } - settingTicksSubscribe(options: ValueSourceTypeConfig[]): number[] { + settingTicksSubscribe(options: ValueSourceConfig[]): number[] { let ticksDatasource: Datasource[] = []; const predefineTicks: number[] = []; for (const tick of options) { if (tick.type === ValueSourceType.constant && isFinite(tick.value)) { - predefineTicks.push(tick.value); + if (isFinite(tick.value)) { + predefineTicks.push(tick.value); + } else { + predefineTicks.push(null); + } } else { try { - ticksDatasource = ColorProcessor.generateDatasource( - this.ctx, ticksDatasource, tick, {index: predefineTicks.length} - ); + ticksDatasource = generateDatasource(this.ctx, ticksDatasource, tick, predefineTicks.length); } catch (e) { continue; } @@ -339,49 +243,22 @@ export class TbCanvasDigitalGauge { } } - this.subscribeAttributes(ticksDatasource, 'ticks').subscribe((subscription) => { - this.ticksSourcesSubscription = subscription; - }); - - return predefineTicks; - } - - subscribeAttributes(datasource: Datasource[], typeAttributes: attributesGaugeType): Observable { - if (!datasource.length) { - return EMPTY; + if (ticksDatasource.length) { + subscribeForDatasource(this.ctx, ticksDatasource, this.updateAttribute.bind(this)).subscribe((subscription) => { + this.ticksSourcesSubscription = subscription; + }); } - const levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { - datasources: datasource, - useDashboardTimewindow: false, - type: widgetType.latest, - callbacks: { - onDataUpdated: (subscription) => { - this.updateAttribute(subscription.data, typeAttributes); - } - } - }; - - return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true); + return predefineTicks; } - updateAttribute(data: Array, typeAttributes: attributesGaugeType) { - for (const keyData of data) { + updateAttribute(subscription: IWidgetSubscription) { + for (const keyData of subscription.data) { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings.dataKeySettings) { - switch (typeAttributes) { - case 'levelColors': - this.localSettings.levelColors[setting.index] = { - value: attrValue, - color: setting.color - }; - break; - case 'ticks': - this.localSettings.ticks[setting.index] = attrValue; - break; - } + for (const index of keyData.dataKey.settings.keyIds) { + this.localSettings.ticks[index] = attrValue; } } } @@ -391,8 +268,7 @@ export class TbCanvasDigitalGauge { updateSetting() { (this.gauge.options as CanvasDigitalGaugeOptions).ticks = this.localSettings.ticks; - (this.gauge.options as CanvasDigitalGaugeOptions).levelColors = this.localSettings.levelColors; - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options as CanvasDigitalGaugeOptions); this.gauge.update({} as CanvasDigitalGaugeOptions); } @@ -402,7 +278,7 @@ export class TbCanvasDigitalGauge { if (cellData.data.length > 0) { const tvPair = cellData.data[cellData.data.length - 1]; - let timestamp; + let timestamp: number; if (this.localSettings.showTimestamp) { timestamp = tvPair[0]; const filter = this.ctx.$injector.get(DatePipe); @@ -415,13 +291,6 @@ export class TbCanvasDigitalGauge { this.gauge._value = value; } this.gauge.value = value; - if (this.localSettings.barColor?.type === ColorType.function && isDefined(this.localSettings.barColor?.colorFunction)) { - this.localSettings.levelColors = [safeExecute(parseFunction(this.localSettings.barColor.colorFunction, ['value']), [value])]; - this.updateSetting(); - } else if (this.localSettings.barColor?.type === ColorType.constant && isDefinedAndNotNull(this.localSettings.barColor?.color)) { - this.localSettings.levelColors = [this.localSettings.barColor.color]; - this.updateSetting(); - } } else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) { this.gauge.timestamp = timestamp; } @@ -440,6 +309,11 @@ export class TbCanvasDigitalGauge { destroy() { this.gauge.destroy(); + this.barColorProcessor.destroy(); + if (this.ticksSourcesSubscription) { + this.ctx.subscriptionApi.removeSubscription(this.ticksSourcesSubscription.id); + } + this.updatedBarColor$?.unsubscribe(); this.gauge = null; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index 0ed2e76687..4387bb71e5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -24,6 +24,7 @@ import { isDefined, isNumber } from '@core/utils'; import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import tinycolor from 'tinycolor2'; import { ResizeObserver } from '@juggle/resize-observer'; +import { ColorProcessor, gradientColor } from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; interface KnobSettings { @@ -158,17 +159,16 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { neonGlowBrightness: 0, gaugeWidthScale: 0.4, gaugeColor: 'rgba(0, 0, 0, 0)', - levelColors, minValue: this.minValue, maxValue: this.maxValue, gaugeType: 'donut', dashThickness: 2, donutStartAngle: 3 / 4 * Math.PI, donutEndAngle: 9 / 4 * Math.PI, - animation: false + animation: false, + barColorProcessor: ColorProcessor.fromSettings(gradientColor('rgba(0, 0, 0, 0)', levelColors, this.minValue, this.maxValue), this.ctx) }; - this.knob.on('click', (e) => { if (this.moving) { this.moving = false; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index 548941bb45..9f55ad90a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -35,8 +35,8 @@ import { ColorType, constantColor, simpleDateFormat, - ValueSourceType, - ValueSourceTypeConfig + ValueSourceConfig, + ValueSourceType } from '@shared/models/widget-settings.models'; @Component({ @@ -348,7 +348,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent settingsForm.setControl('ticksValue', this.prepareTicksValueFormArray(settings.ticksValue), {emitEvent: false}); } - private prepareTicksValueFormArray(ticksValue: ValueSourceTypeConfig[] | undefined): UntypedFormArray { + private prepareTicksValueFormArray(ticksValue: ValueSourceConfig[] | undefined): UntypedFormArray { const ticksValueControls: Array = []; if (ticksValue) { ticksValue.forEach((tickValue) => { @@ -371,7 +371,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } public addTickValue() { - const tickValue: ValueSourceTypeConfig = { + const tickValue: ValueSourceConfig = { type: ValueSourceType.constant }; const tickValuesArray = this.digitalGaugeWidgetSettingsForm.get('ticksValue') as UntypedFormArray; diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 79d17aed88..1f314f6d2a 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -40,7 +40,11 @@ import moment from 'moment'; import tinycolor from 'tinycolor2'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { + IWidgetSubscription, + WidgetSubscriptionCallbacks, + WidgetSubscriptionOptions +} from '@core/api/widget-api.models'; export type ComponentStyle = {[klass: string]: any}; @@ -84,28 +88,6 @@ export interface Font { lineHeight: string; } -export enum ColorType { - constant = 'constant', - gradient = 'gradient', - range = 'range', - function = 'function' -} - -export const colorTypeTranslations = new Map( - [ - [ColorType.constant, 'widgets.color.color-type-constant'], - [ColorType.gradient, 'widgets.color.color-type-gradient'], - [ColorType.range, 'widgets.color.color-type-range'], - [ColorType.function, 'widgets.color.color-type-function'] - ] -); - -export interface ColorRange { - from?: number; - to?: number; - color: string; -} - export enum ValueSourceType { constant = 'constant', latestKey = 'latestKey', @@ -122,7 +104,7 @@ export const ValueSourceTypeTranslation = new Map( ] ); -export interface ValueSourceTypeConfig { +export interface ValueSourceConfig { type: ValueSourceType; value?: number; latestKeyType?: DataKeyType.attribute | DataKeyType.timeseries; @@ -132,12 +114,43 @@ export interface ValueSourceTypeConfig { entityKey?: string; } +export enum ColorType { + constant = 'constant', + gradient = 'gradient', + range = 'range', + function = 'function' +} + +export const colorTypeTranslations = new Map( + [ + [ColorType.constant, 'widgets.color.color-type-constant'], + [ColorType.gradient, 'widgets.color.color-type-gradient'], + [ColorType.range, 'widgets.color.color-type-range'], + [ColorType.function, 'widgets.color.color-type-function'] + ] +); + +interface AdvancedColorMode { + advancedMode?: boolean; +} + +export interface ColorRange { + from?: number; + to?: number; + color: string; +} + export interface AdvancedColorRange { - from?: ValueSourceTypeConfig; - to?: ValueSourceTypeConfig; + from?: ValueSourceConfig; + to?: ValueSourceConfig; color: string; } +export interface ColorRangeSettings extends AdvancedColorMode { + range?: ColorRange[]; + rangeAdvanced?: AdvancedColorRange[]; +} + export const colorRangeIncludes = (range: ColorRange, toCheck: ColorRange): boolean => { if (isNumber(range.from) && isNumber(range.to)) { if (isNumber(toCheck.from) && isNumber(toCheck.to)) { @@ -211,36 +224,24 @@ export const sortedColorRange = (ranges: Array): Array = } ) : []; -export interface ColorSettings { - type: ColorType; - color: string; - rangeList?: ColorRangeSettings; - gradient?: ColorGradientSettings; - colorFunction?: string; -} - -export interface ColorRangeSettings { - advancedMode?: boolean; - range?: ColorRange[]; - rangeAdvanced?: AdvancedColorRange[]; +export interface ColorGradientSettings extends AdvancedColorMode { + gradient?: string[]; + gradientAdvanced?: AdvancedGradient[]; + minValue?: number; + maxValue?: number; } export interface AdvancedGradient { - source: ValueSourceTypeConfig; + source: ValueSourceConfig; color: string; } -export interface ColorGradientSettings { - advancedMode?: boolean; - gradient?: Array; - gradientAdvanced?: Array; - minValue?: number; - maxValue?: number; -} - -export interface DataKeySettings { - color?: string; - index: number; +export interface ColorSettings { + type: ColorType; + color: string; + rangeList?: ColorRangeSettings; + gradient?: ColorGradientSettings; + colorFunction?: string; } export interface TimewindowStyle { @@ -267,6 +268,18 @@ export const constantColor = (color: string): ColorSettings => ({ colorFunction: defaultColorFunction }); +export const gradientColor = (defaultColor: string, colors: string[], minValue?: number, maxValue?: number): ColorSettings => ({ + type: ColorType.gradient, + color: defaultColor, + colorFunction: defaultColorFunction, + gradient: { + advancedMode: false, + gradient: colors, + minValue: isDefinedAndNotNull(minValue) ? minValue : 0, + maxValue: isDefinedAndNotNull(maxValue) ? maxValue : 100 + } +}); + export const defaultGradient = (minValue?: number, maxValue?: number): ColorGradientSettings => ({ advancedMode: false, gradient: ['rgba(0, 255, 0, 1)', 'rgba(255, 0, 0, 1)'], @@ -365,74 +378,6 @@ export abstract class ColorProcessor { abstract update(value: any): void; abstract destroy(): void; - - static generateDatasource( - ctx: WidgetContext, - datasources: Datasource[], - valueSource: ValueSourceTypeConfig, - settings: DataKeySettings): Datasource[]{ - if (valueSource.type === ValueSourceType.latestKey) { - if (ctx.datasources.length) { - if (isDefinedAndNotNull(ctx.datasources[0].aliasName)) { - datasources = this.parseDatasource(ctx, valueSource, ctx.datasources[0].aliasName, settings, datasources, true); - } else { - datasources = this.parseDatasource(ctx, valueSource, '', settings, datasources, true); - } - } - } else if (valueSource.type === ValueSourceType.entity) { - datasources = this.parseDatasource(ctx, valueSource, valueSource.entityAlias, settings, datasources, false); - } - return datasources; - } - - static parseDatasource(ctx: WidgetContext, - valueSource: ValueSourceTypeConfig, - entityAlias: string, - settings: DataKeySettings, - datasources: Datasource[], - isLatest: boolean) { - const dataKeySettings = [settings]; - const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); - let datasource = datasources.find(d => - entityAlias ? (d.entityAliasId === entityAliasId) : (d.deviceId === ctx.datasources[0].deviceId) - ); - const dataKey: DataKey = { - type: isLatest ? valueSource.latestKeyType : valueSource.entityKeyType, - name: isLatest ? valueSource.latestKey : valueSource.entityKey, - label: isLatest ? valueSource.latestKey : valueSource.entityKey, - settings: {dataKeySettings} - }; - if (datasource) { - const findDataKey = datasource.dataKeys.find(dataKeyIteration => - dataKeyIteration.name === (isLatest ? valueSource.latestKey : valueSource.entityKey)); - if (findDataKey) { - findDataKey.settings.dataKeySettings.push(settings); - } else { - datasource.dataKeys.push(dataKey); - } - } else { - if (entityAlias) { - datasource = { - type: DatasourceType.entity, - name: entityAlias, - aliasName: entityAlias, - entityAliasId, - dataKeys: [ dataKey ] - }; - } else { - datasource = { - type: DatasourceType.device, - name: ctx.datasources[0].name, - deviceName: ctx.datasources[0].name, - deviceId: ctx.datasources[0].deviceId, - dataKeys: [ dataKey ] - }; - } - datasources.push(datasource); - } - return datasources; - } - } class ConstantColorProcessor extends ColorProcessor { @@ -444,83 +389,88 @@ class ConstantColorProcessor extends ColorProcessor { destroy(): void {} } -class RangeColorProcessor extends ColorProcessor { +export abstract class AdvancedModeColorProcessor extends ColorProcessor { - private advancedRangeSourcesSubscription: IWidgetSubscription; + protected sourcesSubscription: IWidgetSubscription; + protected advancedMode: boolean; + private currentValue: number; - private progress: number; - - constructor(protected settings: ColorSettings, - protected ctx: WidgetContext) { + protected constructor(protected settings: ColorSettings, + protected ctx: WidgetContext) { super(settings); + this.advancedMode = this.getCurrentConfig().advancedMode; + if (this.advancedMode) { + this.subscribe(); + } } - private advancedRangeSubscribe(options: ColorRangeSettings){ - let advancedRangeDatasource: Datasource[] = []; + abstract updatedAdvancedData(data: Array); + abstract datasourceConfigs(): Array; + abstract getCurrentConfig(): AdvancedColorMode; + + update(value: any) { + if (this.advancedMode) { + this.currentValue = value; + } + } + + destroy(): void { + if (this.sourcesSubscription) { + this.ctx.subscriptionApi.removeSubscription(this.sourcesSubscription.id); + } + } + + private subscribe(){ + let sourcesDatasource: Datasource[] = []; let index = 0; - function setRangeColor(rangeSetting: ValueSourceTypeConfig, color: string) { - if (rangeSetting.type === ValueSourceType.constant && isFinite(rangeSetting.value)) { - index++; - } else { + this.datasourceConfigs().forEach(config => { + if (config.type !== ValueSourceType.constant) { try { - advancedRangeDatasource = ColorProcessor.generateDatasource(this.ctx, advancedRangeDatasource, - rangeSetting, {color, index}); + sourcesDatasource = generateDatasource(this.ctx, sourcesDatasource, config, index); } catch (e) { return; } - index++; } - } + index++; + }); - for (const range of options.rangeAdvanced) { - if (range.from) { - setRangeColor.call(this, range.from, range.color); - } - if (range.to) { - setRangeColor.call(this, range.to, range.color); - } + if (sourcesDatasource.length) { + subscribeForDatasource(this.ctx, sourcesDatasource, this.onDataUpdated.bind(this)).subscribe((subscription) => { + this.sourcesSubscription = subscription; + }); } + } - if (advancedRangeDatasource.length) { - this.subscribeAttributes(advancedRangeDatasource).subscribe((subscription) => { - this.advancedRangeSourcesSubscription = subscription; - }); - } else { - this.color = this.computeFromRange(this.progress); + private onDataUpdated(subscription: IWidgetSubscription) { + this.updatedAdvancedData(subscription.data); + if (this.currentValue) { + this.update(this.currentValue); this.colorUpdated.emit(); } } +} - private subscribeAttributes(datasource: Datasource[]): Observable { - if (!datasource.length) { - return EMPTY; - } +class RangeColorProcessor extends AdvancedModeColorProcessor { - const levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { - datasources: datasource, - useDashboardTimewindow: false, - type: widgetType.latest, - callbacks: { - onDataUpdated: (subscription) => { - this.updateAttribute(subscription.data); - this.color = this.computeFromRange(this.progress); - this.colorUpdated.emit(); - } - } - }; + constructor(protected settings: ColorSettings, + protected ctx: WidgetContext) { + super(settings, ctx); + } - return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true); + update(value: any): void { + super.update(value); + this.color = this.computeFromRange(value); } - private updateAttribute(data: Array) { + updatedAdvancedData(data: Array) { for (const keyData of data) { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings.dataKeySettings) { - const index = Math.floor(setting.index/2); - if (index === setting.index/2) { + for (const id of keyData.dataKey.settings.keyIds) { + const index = Math.floor(id / 2); + if (index === id / 2) { this.settings.rangeList.rangeAdvanced[index].from.value = attrValue; } else { this.settings.rangeList.rangeAdvanced[index].to.value = attrValue; @@ -531,19 +481,21 @@ class RangeColorProcessor extends ColorProcessor { } } - update(value: any): void { - this.progress = value; - if(isDefinedAndNotNull(this.settings.rangeList?.advancedMode) && this.settings.rangeList?.advancedMode) { - this.advancedRangeSubscribe(this.settings.rangeList); - } else { - this.color = this.computeFromRange(value); + datasourceConfigs(): Array { + const configs: Array = []; + for (const range of this.settings.rangeList.rangeAdvanced) { + if (range.from) { + configs.push(range.from); + } + if (range.to) { + configs.push(range.to); + } } + return configs; } - destroy(): void { - if (this.advancedRangeSourcesSubscription) { - this.ctx.subscriptionApi.removeSubscription(this.advancedRangeSourcesSubscription.id); - } + getCurrentConfig(): AdvancedColorMode { + return this.settings.rangeList; } private computeFromRange(value: any): string { @@ -602,35 +554,47 @@ class FunctionColorProcessor extends ColorProcessor { destroy(): void {} } -class GradientColorProcessor extends ColorProcessor { - - private progress: number; +class GradientColorProcessor extends AdvancedModeColorProcessor { private readonly minValue: number; private readonly maxValue: number; - private advancedGradientSourcesSubscription: IWidgetSubscription; - constructor(protected settings: ColorSettings, protected ctx: WidgetContext) { - super(settings); + super(settings, ctx); this.minValue = isDefinedAndNotNull(settings.gradient.minValue) ? settings.gradient.minValue : 0; this.maxValue = isDefinedAndNotNull(settings.gradient.maxValue) ? settings.gradient.maxValue : 100; } update(value: any): void { - this.progress = this.calculateProgress(+value, this.minValue, this.maxValue); - if(isDefinedAndNotNull(this.settings.gradient.advancedMode) && this.settings.gradient.advancedMode) { - this.advancedRangeSubscribe(this.settings.gradient.gradientAdvanced); - } else { - this.color = this.getProgressColor(this.progress, this.settings.gradient.gradient); + const progress = this.calculateProgress(+value, this.minValue, this.maxValue); + super.update(progress); + this.color = this.getGradientColor(progress, this.settings.gradient.gradient); + } + + updatedAdvancedData(data: Array) { + for (const keyData of data) { + if (keyData && keyData.data && keyData.data[0]) { + const attrValue = keyData.data[0][1]; + if (isFinite(attrValue)) { + for (const id of keyData.dataKey.settings.keyIds) { + this.settings.gradient.gradientAdvanced[id].source.value = attrValue; + } + } + } } } - destroy(): void { - if (this.advancedGradientSourcesSubscription) { - this.ctx.subscriptionApi.removeSubscription(this.advancedGradientSourcesSubscription.id); + datasourceConfigs(): Array { + const configs: Array = []; + for (const gradient of this.settings.gradient.gradientAdvanced) { + configs.push(gradient.source); } + return configs; + } + + getCurrentConfig(): AdvancedColorMode { + return this.settings.gradient; } private calculateProgress(current: number, min: number, max: number) { @@ -642,7 +606,7 @@ class GradientColorProcessor extends ColorProcessor { return (current - min) / (max - min); } - private getProgressColor(progress: number, levelColors: Array): string { + private getGradientColor(progress: number, levelColors: Array): string { const colorsCount = levelColors.length; const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; const colorsRange = []; @@ -682,72 +646,6 @@ class GradientColorProcessor extends ColorProcessor { } return colorsRange[colorsRange.length - 1].rgbString; } - - private advancedRangeSubscribe(options: Array){ - let advancedGradientDatasource: Datasource[] = []; - let index = 0; - - function setGradientColor(gradientSetting: ValueSourceTypeConfig) { - if (gradientSetting.type === ValueSourceType.constant && isFinite(gradientSetting.value)) { - index++; - } else { - try { - advancedGradientDatasource = this.generateDatasource(this.ctx, advancedGradientDatasource, - gradientSetting, {index}); - } catch (e) { - return; - } - index++; - } - } - - for (const gradient of options) { - setGradientColor.call(this, gradient.source); - } - - if (advancedGradientDatasource.length) { - this.subscribeAttributes(advancedGradientDatasource).subscribe((subscription) => { - this.advancedGradientSourcesSubscription = subscription; - }); - } else { - this.color = this.getProgressColor(this.progress, this.settings.gradient.gradientAdvanced); - this.colorUpdated.emit(); - } - } - - private subscribeAttributes(datasource: Datasource[]): Observable { - if (!datasource.length) { - return EMPTY; - } - - const levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { - datasources: datasource, - useDashboardTimewindow: false, - type: widgetType.latest, - callbacks: { - onDataUpdated: (subscription) => { - this.updateAttribute(subscription.data); - this.color = this.getProgressColor(this.progress, this.settings.gradient.gradientAdvanced); - this.colorUpdated.emit(); - } - } - }; - - return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true); - } - - private updateAttribute(data: Array) { - for (const keyData of data) { - if (keyData && keyData.data && keyData.data[0]) { - const attrValue = keyData.data[0][1]; - if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings.dataKeySettings) { - this.settings.gradient.gradientAdvanced[setting.index].source.value = attrValue; - } - } - } - } - } } export type FormatTimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' @@ -1261,3 +1159,87 @@ export const getLatestSingleTsValue = (data: Array): DataEntry = } return null; }; + +export const generateDatasource = (ctx: WidgetContext, + datasources: Datasource[], + valueSource: ValueSourceConfig, + index: number): Datasource[] => { + if (valueSource.type === ValueSourceType.latestKey) { + if (ctx.datasources.length && isDefinedAndNotNull(ctx.datasources[0].aliasName)) { + datasources = configureDatasource(ctx, valueSource, ctx.datasources[0].aliasName, datasources, index, true); + } else { + datasources = configureDatasource(ctx, valueSource, '', datasources, index, true); + } + } else if (valueSource.type === ValueSourceType.entity) { + datasources = configureDatasource(ctx, valueSource, valueSource.entityAlias, datasources, index, false); + } + return datasources; +}; + +export const configureDatasource = (ctx: WidgetContext, + valueSource: ValueSourceConfig, + entityAlias: string, + datasources: Datasource[], + index: number, + isLatest: boolean): Datasource[] => { + const keyIds = [index]; + const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); + let datasource = datasources.find(d => + entityAlias ? (d.entityAliasId === entityAliasId) : (d.deviceId === ctx.datasources[0].deviceId) + ); + const dataKey: DataKey = { + type: isLatest ? valueSource.latestKeyType : valueSource.entityKeyType, + name: isLatest ? valueSource.latestKey : valueSource.entityKey, + label: isLatest ? valueSource.latestKey : valueSource.entityKey, + settings: {keyIds} + }; + if (datasource) { + const findDataKey = datasource.dataKeys.find(dataKeyIteration => + dataKeyIteration.name === (isLatest ? valueSource.latestKey : valueSource.entityKey)); + if (findDataKey) { + findDataKey.settings.keyIds.push(index); + } else { + datasource.dataKeys.push(dataKey); + } + } else { + if (entityAlias) { + datasource = { + type: DatasourceType.entity, + name: entityAlias, + aliasName: entityAlias, + entityAliasId, + dataKeys: [dataKey] + }; + } else { + datasource = { + type: DatasourceType.device, + name: ctx.datasources[0].name, + deviceName: ctx.datasources[0].name, + deviceId: ctx.datasources[0].deviceId, + dataKeys: [dataKey] + }; + } + datasources.push(datasource); + } + return datasources; +}; + + +export const subscribeForDatasource = (ctx: WidgetContext, + datasource: Datasource[], + onDataUpdated: WidgetSubscriptionCallbacks['onDataUpdated']): Observable => { + if (!datasource.length) { + return EMPTY; + } + + const subscriptionOptions: WidgetSubscriptionOptions = { + datasources: datasource, + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated + } + }; + + return ctx.subscriptionApi.createSubscription(subscriptionOptions, true); +};