Browse Source

UI: Refactoring ColorProcessor and added to gauge bar used ColorProcessor

pull/10557/head
Vladyslav_Prykhodko 2 years ago
parent
commit
59066a3ef0
  1. 27
      ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts
  2. 152
      ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts
  3. 6
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
  4. 2
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
  5. 47
      ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts
  6. 214
      ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts
  7. 6
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts
  8. 8
      ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts
  9. 500
      ui-ngx/src/app/shared/models/widget-settings.models.ts

27
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), []],

152
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<string | ColorLevelSetting>;
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} =

6
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;

2
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;
}

47
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<string | ColorLevelSetting>;
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;
}
}
};

214
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<IWidgetSubscription> {
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<DatasourceData>, 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;
}
}

6
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;

8
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<AbstractControl> = [];
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;

500
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, string>(
[
[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<ValueSourceType, string>(
]
);
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, string>(
[
[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<ColorRange>): Array<ColorRange> =
}
) : [];
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<string>;
gradientAdvanced?: Array<AdvancedGradient>;
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<DatasourceData>);
abstract datasourceConfigs(): Array<ValueSourceConfig>;
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<IWidgetSubscription> {
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<DatasourceData>) {
updatedAdvancedData(data: Array<DatasourceData>) {
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<ValueSourceConfig> {
const configs: Array<ValueSourceConfig> = [];
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<DatasourceData>) {
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<ValueSourceConfig> {
const configs: Array<ValueSourceConfig> = [];
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<AdvancedGradient | string>): string {
private getGradientColor(progress: number, levelColors: Array<AdvancedGradient | string>): 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<AdvancedGradient>){
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<IWidgetSubscription> {
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<DatasourceData>) {
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<DatasourceData>): 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<IWidgetSubscription> => {
if (!datasource.length) {
return EMPTY;
}
const subscriptionOptions: WidgetSubscriptionOptions = {
datasources: datasource,
useDashboardTimewindow: false,
type: widgetType.latest,
callbacks: {
onDataUpdated
}
};
return ctx.subscriptionApi.createSubscription(subscriptionOptions, true);
};

Loading…
Cancel
Save