Commit 4e16675ea43dc4b3370b1830a070ee6c259545d1

Authored by Vladyslav
Committed by GitHub
1 parent 5933397e

Add change gauge color (#2480)

@@ -20,7 +20,7 @@ import BaseGauge = CanvasGauges.BaseGauge; @@ -20,7 +20,7 @@ import BaseGauge = CanvasGauges.BaseGauge;
20 import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models'; 20 import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models';
21 import * as tinycolor_ from 'tinycolor2'; 21 import * as tinycolor_ from 'tinycolor2';
22 import { ColorFormats } from 'tinycolor2'; 22 import { ColorFormats } from 'tinycolor2';
23 -import { isDefined, isUndefined } from '@core/utils'; 23 +import { isDefined, isString, isUndefined } from '@core/utils';
24 24
25 const tinycolor = tinycolor_; 25 const tinycolor = tinycolor_;
26 26
@@ -32,13 +32,20 @@ export interface DigitalGaugeColorRange { @@ -32,13 +32,20 @@ export interface DigitalGaugeColorRange {
32 rgbString: string; 32 rgbString: string;
33 } 33 }
34 34
  35 +export interface colorLevelSetting {
  36 + value: number;
  37 + color: string;
  38 +}
  39 +
  40 +export type levelColors = Array<string | colorLevelSetting>;
  41 +
35 export interface CanvasDigitalGaugeOptions extends GenericOptions { 42 export interface CanvasDigitalGaugeOptions extends GenericOptions {
36 gaugeType?: GaugeType; 43 gaugeType?: GaugeType;
37 gaugeWithScale?: number; 44 gaugeWithScale?: number;
38 dashThickness?: number; 45 dashThickness?: number;
39 roundedLineCap?: boolean; 46 roundedLineCap?: boolean;
40 gaugeColor?: string; 47 gaugeColor?: string;
41 - levelColors?: string[]; 48 + levelColors?: levelColors;
42 symbol?: string; 49 symbol?: string;
43 label?: string; 50 label?: string;
44 hideValue?: boolean; 51 hideValue?: boolean;
@@ -229,26 +236,30 @@ export class CanvasDigitalGauge extends BaseGauge { @@ -229,26 +236,30 @@ export class CanvasDigitalGauge extends BaseGauge {
229 } 236 }
230 237
231 const colorsCount = options.levelColors.length; 238 const colorsCount = options.levelColors.length;
  239 + const isColorProperty = isString(options.levelColors[0]);
232 const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; 240 const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1;
233 options.colorsRange = []; 241 options.colorsRange = [];
234 if (options.neonGlowBrightness) { 242 if (options.neonGlowBrightness) {
235 options.neonColorsRange = []; 243 options.neonColorsRange = [];
236 } 244 }
237 for (let i = 0; i < options.levelColors.length; i++) { 245 for (let i = 0; i < options.levelColors.length; i++) {
238 - const percentage = inc * i;  
239 - let tColor = tinycolor(options.levelColors[i]);  
240 - options.colorsRange[i] = {  
241 - pct: percentage,  
242 - color: tColor.toRgb(),  
243 - rgbString: tColor.toRgbString()  
244 - };  
245 - if (options.neonGlowBrightness) {  
246 - tColor = tinycolor(options.levelColors[i]).brighten(options.neonGlowBrightness);  
247 - options.neonColorsRange[i] = { 246 + let levelColor: any = options.levelColors[i];
  247 + if (levelColor !== null) {
  248 + let percentage = isColorProperty ? inc * i : CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue);
  249 + let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color);
  250 + options.colorsRange[i] = {
248 pct: percentage, 251 pct: percentage,
249 color: tColor.toRgb(), 252 color: tColor.toRgb(),
250 rgbString: tColor.toRgbString() 253 rgbString: tColor.toRgbString()
251 }; 254 };
  255 + if (options.neonGlowBrightness) {
  256 + tColor = tinycolor(isColorProperty ? levelColor : levelColor.color).brighten(options.neonGlowBrightness);
  257 + options.neonColorsRange[i] = {
  258 + pct: percentage,
  259 + color: tColor.toRgb(),
  260 + rgbString: tColor.toRgbString()
  261 + };
  262 + }
252 } 263 }
253 } 264 }
254 265
@@ -262,6 +273,17 @@ export class CanvasDigitalGauge extends BaseGauge { @@ -262,6 +273,17 @@ export class CanvasDigitalGauge extends BaseGauge {
262 return options; 273 return options;
263 } 274 }
264 275
  276 + static normalizeValue (value: number, min: number, max: number): number {
  277 + let normalValue = (value - min) / (max - min);
  278 + if (normalValue <= 0) {
  279 + return 0;
  280 + }
  281 + if (normalValue >= 1) {
  282 + return 1;
  283 + }
  284 + return normalValue;
  285 + }
  286 +
265 private initValueClone() { 287 private initValueClone() {
266 const canvas = this.canvas; 288 const canvas = this.canvas;
267 this.elementValueClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone; 289 this.elementValueClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone;
@@ -19,6 +19,26 @@ import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; @@ -19,6 +19,26 @@ import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge';
19 import { AnimationRule } from '@home/components/widget/lib/analogue-gauge.models'; 19 import { AnimationRule } from '@home/components/widget/lib/analogue-gauge.models';
20 import { FontSettings } from '@home/components/widget/lib/settings.models'; 20 import { FontSettings } from '@home/components/widget/lib/settings.models';
21 21
  22 +export interface colorLevelProperty {
  23 + valueSource: string;
  24 + entityAlias?: string;
  25 + attribute?: string;
  26 + value?: number;
  27 +}
  28 +
  29 +export interface fixedLevelColors {
  30 + from?: colorLevelProperty;
  31 + to?: colorLevelProperty;
  32 + color: string;
  33 +}
  34 +
  35 +export interface colorLevelSetting {
  36 + value: number;
  37 + color: string;
  38 +}
  39 +
  40 +export type colorLevel = Array<string | colorLevelSetting>;
  41 +
22 export interface DigitalGaugeSettings { 42 export interface DigitalGaugeSettings {
23 minValue?: number; 43 minValue?: number;
24 maxValue?: number; 44 maxValue?: number;
@@ -38,7 +58,9 @@ export interface DigitalGaugeSettings { @@ -38,7 +58,9 @@ export interface DigitalGaugeSettings {
38 gaugeWidthScale?: number; 58 gaugeWidthScale?: number;
39 defaultColor?: string; 59 defaultColor?: string;
40 gaugeColor?: string; 60 gaugeColor?: string;
41 - levelColors?: string[]; 61 + useFixedLevelColor?: boolean;
  62 + levelColors?: colorLevel;
  63 + fixedLevelColors?: fixedLevelColors[];
42 animation?: boolean; 64 animation?: boolean;
43 animationDuration?: number; 65 animationDuration?: number;
44 animationRule?: AnimationRule; 66 animationRule?: AnimationRule;
@@ -147,6 +169,11 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -147,6 +169,11 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
147 type: 'string', 169 type: 'string',
148 default: null 170 default: null
149 }, 171 },
  172 + useFixedLevelColor: {
  173 + title: 'Use precise value for the color indicator',
  174 + type: 'boolean',
  175 + default: false
  176 + },
150 levelColors: { 177 levelColors: {
151 title: 'Colors of indicator, from lower to upper', 178 title: 'Colors of indicator, from lower to upper',
152 type: 'array', 179 type: 'array',
@@ -155,6 +182,66 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -155,6 +182,66 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
155 type: 'string' 182 type: 'string'
156 } 183 }
157 }, 184 },
  185 + fixedLevelColors: {
  186 + title: 'The colors for the indicator using boundary values',
  187 + type: 'array',
  188 + items: {
  189 + title: 'levelColor',
  190 + type: 'object',
  191 + properties: {
  192 + from: {
  193 + title: 'From',
  194 + type: 'object',
  195 + properties: {
  196 + valueSource: {
  197 + title: '[From] Value source',
  198 + type: 'string',
  199 + default: 'predefinedValue'
  200 + },
  201 + entityAlias: {
  202 + title: '[From] Source entity alias',
  203 + type: 'string'
  204 + },
  205 + attribute: {
  206 + title: '[From] Source entity attribute',
  207 + type: 'string'
  208 + },
  209 + value: {
  210 + title: '[From] Value (if predefined value is selected)',
  211 + type: 'number'
  212 + }
  213 + }
  214 + },
  215 + to: {
  216 + title: 'To',
  217 + type: 'object',
  218 + properties: {
  219 + valueSource: {
  220 + title: '[To] Value source',
  221 + type: 'string',
  222 + default: 'predefinedValue'
  223 + },
  224 + entityAlias: {
  225 + title: '[To] Source entity alias',
  226 + type: 'string'
  227 + },
  228 + attribute: {
  229 + title: '[To] Source entity attribute',
  230 + type: 'string'
  231 + },
  232 + value: {
  233 + title: '[To] Value (if predefined value is selected)',
  234 + type: 'number'
  235 + }
  236 + }
  237 + },
  238 + color: {
  239 + title: 'Color',
  240 + type: 'string'
  241 + }
  242 + }
  243 + }
  244 + },
158 animation: { 245 animation: {
159 title: 'Enable animation', 246 title: 'Enable animation',
160 type: 'boolean', 247 type: 'boolean',
@@ -343,8 +430,10 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -343,8 +430,10 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
343 key: 'gaugeColor', 430 key: 'gaugeColor',
344 type: 'color' 431 type: 'color'
345 }, 432 },
  433 + 'useFixedLevelColor',
346 { 434 {
347 key: 'levelColors', 435 key: 'levelColors',
  436 + condition: 'model.useFixedLevelColor !== true',
348 items: [ 437 items: [
349 { 438 {
350 key: 'levelColors[]', 439 key: 'levelColors[]',
@@ -352,6 +441,52 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -352,6 +441,52 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
352 } 441 }
353 ] 442 ]
354 }, 443 },
  444 + {
  445 + key: 'fixedLevelColors',
  446 + condition: 'model.useFixedLevelColor === true',
  447 + items: [
  448 + {
  449 + key: 'fixedLevelColors[].from.valueSource',
  450 + type: 'rc-select',
  451 + multiple: false,
  452 + items: [
  453 + {
  454 + value: 'predefinedValue',
  455 + label: 'Predefined value (Default)'
  456 + },
  457 + {
  458 + value: 'entityAttribute',
  459 + label: 'Value taken from entity attribute'
  460 + }
  461 + ]
  462 + },
  463 + 'fixedLevelColors[].from.value',
  464 + 'fixedLevelColors[].from.entityAlias',
  465 + 'fixedLevelColors[].from.attribute',
  466 + {
  467 + key: 'fixedLevelColors[].to.valueSource',
  468 + type: 'rc-select',
  469 + multiple: false,
  470 + items: [
  471 + {
  472 + value: 'predefinedValue',
  473 + label: 'Predefined value (Default)'
  474 + },
  475 + {
  476 + value: 'entityAttribute',
  477 + label: 'Value taken from entity attribute'
  478 + }
  479 + ]
  480 + },
  481 + 'fixedLevelColors[].to.value',
  482 + 'fixedLevelColors[].to.entityAlias',
  483 + 'fixedLevelColors[].to.attribute',
  484 + {
  485 + key: 'fixedLevelColors[].color',
  486 + type: 'color'
  487 + }
  488 + ]
  489 + },
355 'animation', 490 'animation',
356 'animationDuration', 491 'animationDuration',
357 { 492 {
@@ -16,14 +16,20 @@ @@ -16,14 +16,20 @@
16 16
17 import * as CanvasGauges from 'canvas-gauges'; 17 import * as CanvasGauges from 'canvas-gauges';
18 import { WidgetContext } from '@home/models/widget-component.models'; 18 import { WidgetContext } from '@home/models/widget-component.models';
19 -import { DigitalGaugeSettings, digitalGaugeSettingsSchema } from '@home/components/widget/lib/digital-gauge.models'; 19 +import {
  20 + colorLevelSetting,
  21 + DigitalGaugeSettings,
  22 + digitalGaugeSettingsSchema
  23 +} from '@home/components/widget/lib/digital-gauge.models';
20 import * as tinycolor_ from 'tinycolor2'; 24 import * as tinycolor_ from 'tinycolor2';
21 import { isDefined } from '@core/utils'; 25 import { isDefined } from '@core/utils';
22 import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; 26 import { prepareFontSettings } from '@home/components/widget/lib/settings.models';
23 import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; 27 import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge';
24 import { DatePipe } from '@angular/common'; 28 import { DatePipe } from '@angular/common';
25 -import { JsonSettingsSchema } from '@shared/models/widget.models'; 29 +import {DataKey, Datasource, DatasourceType, JsonSettingsSchema, widgetType} from '@shared/models/widget.models';
26 import GenericOptions = CanvasGauges.GenericOptions; 30 import GenericOptions = CanvasGauges.GenericOptions;
  31 +import {IWidgetSubscription, WidgetSubscriptionOptions} from "@core/api/widget-api.models";
  32 +import {DataKeyType} from "@shared/models/telemetry/telemetry.models";
27 33
28 const tinycolor = tinycolor_; 34 const tinycolor = tinycolor_;
29 35
@@ -32,6 +38,7 @@ const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema; @@ -32,6 +38,7 @@ const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema;
32 export class TbCanvasDigitalGauge { 38 export class TbCanvasDigitalGauge {
33 39
34 private localSettings: DigitalGaugeSettings; 40 private localSettings: DigitalGaugeSettings;
  41 + private levelColorsSourcesSubscription: IWidgetSubscription;
35 42
36 private gauge: CanvasDigitalGauge; 43 private gauge: CanvasDigitalGauge;
37 44
@@ -65,10 +72,16 @@ export class TbCanvasDigitalGauge { @@ -65,10 +72,16 @@ export class TbCanvasDigitalGauge {
65 this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; 72 this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75;
66 this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); 73 this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString();
67 74
68 - if (!settings.levelColors || settings.levelColors.length <= 0) {  
69 - this.localSettings.levelColors = [keyColor]; 75 + this.localSettings.useFixedLevelColor = settings.useFixedLevelColor || false;
  76 + if (!settings.useFixedLevelColor) {
  77 + if (!settings.levelColors || settings.levelColors.length <= 0) {
  78 + this.localSettings.levelColors = [keyColor];
  79 + } else {
  80 + this.localSettings.levelColors = settings.levelColors.slice();
  81 + }
70 } else { 82 } else {
71 - this.localSettings.levelColors = settings.levelColors.slice(); 83 + this.localSettings.levelColors = [keyColor];
  84 + this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
72 } 85 }
73 86
74 this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals : 87 this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals :
@@ -176,6 +189,130 @@ export class TbCanvasDigitalGauge { @@ -176,6 +189,130 @@ export class TbCanvasDigitalGauge {
176 }; 189 };
177 190
178 this.gauge = new CanvasDigitalGauge(gaugeData).draw(); 191 this.gauge = new CanvasDigitalGauge(gaugeData).draw();
  192 + this.init();
  193 + }
  194 +
  195 + init() {
  196 + if (this.localSettings.useFixedLevelColor) {
  197 + if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
  198 + this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
  199 + this.updateLevelColors(this.localSettings.levelColors);
  200 + }
  201 + }
  202 + }
  203 +
  204 + settingLevelColorsSubscribe(options) {
  205 + let levelColorsDatasource: Datasource[] = [];
  206 + let predefineLevelColors: colorLevelSetting[] = [];
  207 +
  208 + function setLevelColor(levelSetting, color) {
  209 + if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) {
  210 + predefineLevelColors.push({
  211 + value: levelSetting.value,
  212 + color: color
  213 + })
  214 + } else if (levelSetting.entityAlias && levelSetting.attribute) {
  215 + let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias);
  216 + if (!entityAliasId) {
  217 + return;
  218 + }
  219 +
  220 + let datasource = levelColorsDatasource.find((datasource) => {
  221 + return datasource.entityAliasId === entityAliasId;
  222 + });
  223 +
  224 + let dataKey: DataKey = {
  225 + type: DataKeyType.attribute,
  226 + name: levelSetting.attribute,
  227 + label: levelSetting.attribute,
  228 + settings: [{
  229 + color: color,
  230 + index: predefineLevelColors.length
  231 + }],
  232 + _hash: Math.random()
  233 + };
  234 +
  235 + if (datasource) {
  236 + let findDataKey = datasource.dataKeys.find((dataKey) => {
  237 + return dataKey.name === levelSetting.attribute;
  238 + });
  239 +
  240 + if (findDataKey) {
  241 + findDataKey.settings.push({
  242 + color: color,
  243 + index: predefineLevelColors.length
  244 + });
  245 + } else {
  246 + datasource.dataKeys.push(dataKey)
  247 + }
  248 + } else {
  249 + let datasource: Datasource = {
  250 + type: DatasourceType.entity,
  251 + name: levelSetting.entityAlias,
  252 + aliasName: levelSetting.entityAlias,
  253 + entityAliasId: entityAliasId,
  254 + dataKeys: [dataKey]
  255 + };
  256 + levelColorsDatasource.push(datasource);
  257 + }
  258 +
  259 + predefineLevelColors.push(null);
  260 + }
  261 + }
  262 +
  263 + for (let i = 0; i < options.length; i++) {
  264 + let levelColor = options[i];
  265 + if (levelColor.from) {
  266 + setLevelColor.call(this, levelColor.from, levelColor.color);
  267 + }
  268 + if (levelColor.to) {
  269 + setLevelColor.call(this, levelColor.to, levelColor.color);
  270 + }
  271 + }
  272 +
  273 + this.subscribeLevelColorsAttributes(levelColorsDatasource);
  274 +
  275 + return predefineLevelColors;
  276 + }
  277 +
  278 + updateLevelColors(levelColors) {
  279 + (this.gauge.options as CanvasDigitalGaugeOptions).levelColors = levelColors;
  280 + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
  281 + this.gauge.update({} as CanvasDigitalGaugeOptions);
  282 + }
  283 +
  284 + subscribeLevelColorsAttributes(datasources: Datasource[]) {
  285 + let TbCanvasDigitalGauge = this;
  286 + let levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = {
  287 + datasources: datasources,
  288 + useDashboardTimewindow: false,
  289 + type: widgetType.latest,
  290 + callbacks: {
  291 + onDataUpdated: (subscription) => {
  292 + for (let i = 0; i < subscription.data.length; i++) {
  293 + let keyData = subscription.data[i];
  294 + if (keyData && keyData.data && keyData.data[0]) {
  295 + let attrValue = keyData.data[0][1];
  296 + if (isFinite(attrValue)) {
  297 + for (let i = 0; i < keyData.dataKey.settings.length; i++) {
  298 + let setting = keyData.dataKey.settings[i];
  299 + this.localSettings.levelColors[setting.index] = {
  300 + value: attrValue,
  301 + color: setting.color
  302 + };
  303 + }
  304 + }
  305 + }
  306 + }
  307 + this.updateLevelColors(this.localSettings.levelColors);
  308 + }
  309 + }
  310 + };
  311 + this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).subscribe(
  312 + (subscription) => {
  313 + TbCanvasDigitalGauge.levelColorsSourcesSubscription = subscription;
  314 + }
  315 + );
179 } 316 }
180 317
181 update() { 318 update() {
@@ -186,7 +186,6 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato @@ -186,7 +186,6 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
186 val = undefined; 186 val = undefined;
187 } 187 }
188 if (JsonFormUtils.updateValue(key, this.model, val) || forceUpdate) { 188 if (JsonFormUtils.updateValue(key, this.model, val) || forceUpdate) {
189 - this.formProps.model = this.model;  
190 this.isModelValid = this.validateModel(); 189 this.isModelValid = this.validateModel();
191 this.updateView(); 190 this.updateView();
192 } 191 }
@@ -233,7 +232,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato @@ -233,7 +232,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
233 this.formProps.schema = this.schema; 232 this.formProps.schema = this.schema;
234 this.formProps.form = this.form; 233 this.formProps.form = this.form;
235 this.formProps.groupInfoes = this.groupInfoes; 234 this.formProps.groupInfoes = this.groupInfoes;
236 - this.formProps.model = deepClone(this.model); 235 + this.formProps.model = this.model;
237 this.renderReactSchemaForm(); 236 this.renderReactSchemaForm();
238 } 237 }
239 238