Commit adc7194669d4a8ee0b38ac99f37dcb663e8a62ee

Authored by Vladyslav
Committed by GitHub
1 parent c55ddd9f

[3.0] Add support ticks to digital gauge (#2508)

* Add support ticks to digital gauge

* Add link code
@@ -15,12 +15,12 @@ @@ -15,12 +15,12 @@
15 /// 15 ///
16 16
17 import * as CanvasGauges from 'canvas-gauges'; 17 import * as CanvasGauges from 'canvas-gauges';
18 -import GenericOptions = CanvasGauges.GenericOptions;  
19 -import BaseGauge = CanvasGauges.BaseGauge;  
20 import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models'; 18 import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models';
21 import * as tinycolor_ from 'tinycolor2'; 19 import * as tinycolor_ from 'tinycolor2';
22 import { ColorFormats } from 'tinycolor2'; 20 import { ColorFormats } from 'tinycolor2';
23 import { isDefined, isString, isUndefined } from '@core/utils'; 21 import { isDefined, isString, isUndefined } from '@core/utils';
  22 +import GenericOptions = CanvasGauges.GenericOptions;
  23 +import BaseGauge = CanvasGauges.BaseGauge;
24 24
25 const tinycolor = tinycolor_; 25 const tinycolor = tinycolor_;
26 26
@@ -32,12 +32,12 @@ export interface DigitalGaugeColorRange { @@ -32,12 +32,12 @@ export interface DigitalGaugeColorRange {
32 rgbString: string; 32 rgbString: string;
33 } 33 }
34 34
35 -export interface colorLevelSetting { 35 +export interface ColorLevelSetting {
36 value: number; 36 value: number;
37 color: string; 37 color: string;
38 } 38 }
39 39
40 -export type levelColors = Array<string | colorLevelSetting>; 40 +export type levelColors = Array<string | ColorLevelSetting>;
41 41
42 export interface CanvasDigitalGaugeOptions extends GenericOptions { 42 export interface CanvasDigitalGaugeOptions extends GenericOptions {
43 gaugeType?: GaugeType; 43 gaugeType?: GaugeType;
@@ -81,6 +81,11 @@ export interface CanvasDigitalGaugeOptions extends GenericOptions { @@ -81,6 +81,11 @@ export interface CanvasDigitalGaugeOptions extends GenericOptions {
81 fontValueHeight?: FontHeightInfo; 81 fontValueHeight?: FontHeightInfo;
82 fontMinMaxHeight?: FontHeightInfo; 82 fontMinMaxHeight?: FontHeightInfo;
83 83
  84 + ticksValue?: number[];
  85 + ticks?: number[];
  86 + colorTicks?: string;
  87 + tickWidth?: number;
  88 +
84 showTimestamp?: boolean; 89 showTimestamp?: boolean;
85 } 90 }
86 91
@@ -117,6 +122,10 @@ const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOption @@ -117,6 +122,10 @@ const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOption
117 122
118 neonGlowBrightness: 0, 123 neonGlowBrightness: 0,
119 124
  125 + colorTicks: 'gray',
  126 + tickWidth: 4,
  127 + ticks: [],
  128 +
120 isMobile: false 129 isMobile: false
121 } 130 }
122 }; 131 };
@@ -243,9 +252,14 @@ export class CanvasDigitalGauge extends BaseGauge { @@ -243,9 +252,14 @@ export class CanvasDigitalGauge extends BaseGauge {
243 options.neonColorsRange = []; 252 options.neonColorsRange = [];
244 } 253 }
245 for (let i = 0; i < options.levelColors.length; i++) { 254 for (let i = 0; i < options.levelColors.length; i++) {
246 - let levelColor: any = options.levelColors[i]; 255 + const levelColor: any = options.levelColors[i];
247 if (levelColor !== null) { 256 if (levelColor !== null) {
248 - let percentage = isColorProperty ? inc * i : CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue); 257 + let percentage: number;
  258 + if(isColorProperty){
  259 + percentage = inc * i;
  260 + } else {
  261 + percentage = CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue);
  262 + }
249 let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color); 263 let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color);
250 options.colorsRange.push({ 264 options.colorsRange.push({
251 pct: percentage, 265 pct: percentage,
@@ -263,6 +277,13 @@ export class CanvasDigitalGauge extends BaseGauge { @@ -263,6 +277,13 @@ export class CanvasDigitalGauge extends BaseGauge {
263 } 277 }
264 } 278 }
265 279
  280 + options.ticksValue = [];
  281 + for (const tick of options.ticks) {
  282 + if (tick !== null) {
  283 + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(tick, options.minValue, options.maxValue))
  284 + }
  285 + }
  286 +
266 if (options.neonGlowBrightness) { 287 if (options.neonGlowBrightness) {
267 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString(); 288 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
268 options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString(); 289 options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
@@ -274,7 +295,7 @@ export class CanvasDigitalGauge extends BaseGauge { @@ -274,7 +295,7 @@ export class CanvasDigitalGauge extends BaseGauge {
274 } 295 }
275 296
276 static normalizeValue (value: number, min: number, max: number): number { 297 static normalizeValue (value: number, min: number, max: number): number {
277 - let normalValue = (value - min) / (max - min); 298 + const normalValue = (value - min) / (max - min);
278 if (normalValue <= 0) { 299 if (normalValue <= 0) {
279 return 0; 300 return 0;
280 } 301 }
@@ -657,7 +678,7 @@ function determineFontHeight (options: CanvasDigitalGaugeOptions, target: string @@ -657,7 +678,7 @@ function determineFontHeight (options: CanvasDigitalGaugeOptions, target: string
657 fontStyle: options['font' + target + 'Style'] 678 fontStyle: options['font' + target + 'Style']
658 }; 679 };
659 const text = $('<span>Hg</span>').css(fontStyle); 680 const text = $('<span>Hg</span>').css(fontStyle);
660 - const block = $('<div style="display: inline-block; width: 1px; height: 0px;"></div>'); 681 + const block = $('<div style="display: inline-block; width: 1px; height: 0;"></div>');
661 682
662 const div = $('<div></div>'); 683 const div = $('<div></div>');
663 div.append(text, block); 684 div.append(text, block);
@@ -884,6 +905,52 @@ function drawBarGlow(context: DigitalGaugeCanvasRenderingContext2D, startX: numb @@ -884,6 +905,52 @@ function drawBarGlow(context: DigitalGaugeCanvasRenderingContext2D, startX: numb
884 context.stroke(); 905 context.stroke();
885 } 906 }
886 907
  908 +function drawTickArc(context: DigitalGaugeCanvasRenderingContext2D, tickValues: number[], Cx: number, Cy: number,
  909 + Ri: number, Rm: number, Ro: number, startAngle: number, endAngle: number,
  910 + color: string, tickWidth: number) {
  911 + if (!tickValues.length) {
  912 + return;
  913 + }
  914 +
  915 + const strokeWidth = Ro - Ri;
  916 + context.beginPath();
  917 + context.lineWidth = tickWidth;
  918 + context.strokeStyle = color;
  919 + for (const tick of tickValues) {
  920 + const angle = startAngle + tick * endAngle;
  921 + const x1 = Cx + (Ri + strokeWidth) * Math.cos(angle);
  922 + const y1 = Cy + (Ri + strokeWidth) * Math.sin(angle);
  923 + const x2 = Cx + Ri * Math.cos(angle);
  924 + const y2 = Cy + Ri * Math.sin(angle);
  925 + context.moveTo(x1, y1);
  926 + context.lineTo(x2, y2);
  927 + }
  928 + context.stroke();
  929 +}
  930 +
  931 +function drawTickBar(context: DigitalGaugeCanvasRenderingContext2D, tickValues: number[], startX: number, startY: number,
  932 + distanceBar: number, strokeWidth: number, isVertical: boolean, color: string, tickWidth: number) {
  933 + if (!tickValues.length) {
  934 + return;
  935 + }
  936 +
  937 + context.beginPath();
  938 + context.lineWidth = tickWidth;
  939 + context.strokeStyle = color;
  940 + for (const tick of tickValues) {
  941 + const tickValue = tick * distanceBar;
  942 + if (isVertical) {
  943 + context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar);
  944 + context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar);
  945 + } else {
  946 + context.moveTo(startX + tickValue, startY);
  947 + context.lineTo(startX + tickValue, startY + strokeWidth);
  948 + }
  949 + }
  950 + context.stroke();
  951 +}
  952 +
  953 +
887 function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, 954 function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
888 options: CanvasDigitalGaugeOptions, progress: number) { 955 options: CanvasDigitalGaugeOptions, progress: number) {
889 let neonColor; 956 let neonColor;
@@ -917,6 +984,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, @@ -917,6 +984,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
917 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, 984 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true,
918 options.donutStartAngle, options.donutEndAngle); 985 options.donutStartAngle, options.donutEndAngle);
919 } 986 }
  987 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle,
  988 + options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth);
920 } else if (options.gaugeType === 'arc') { 989 } else if (options.gaugeType === 'arc') {
921 if (options.neonGlowBrightness) { 990 if (options.neonGlowBrightness) {
922 context.strokeStyle = neonColor; 991 context.strokeStyle = neonColor;
@@ -927,6 +996,7 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, @@ -927,6 +996,7 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
927 if (options.neonGlowBrightness && !options.isMobile) { 996 if (options.neonGlowBrightness && !options.isMobile) {
928 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false); 997 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
929 } 998 }
  999 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth);
930 } else if (options.gaugeType === 'horizontalBar') { 1000 } else if (options.gaugeType === 'horizontalBar') {
931 if (options.neonGlowBrightness) { 1001 if (options.neonGlowBrightness) {
932 context.strokeStyle = neonColor; 1002 context.strokeStyle = neonColor;
@@ -940,6 +1010,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, @@ -940,6 +1010,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
940 barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, 1010 barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
941 neonColor, strokeWidth, false); 1011 neonColor, strokeWidth, false);
942 } 1012 }
  1013 + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth,
  1014 + false, options.colorTicks, options.tickWidth);
943 } else if (options.gaugeType === 'verticalBar') { 1015 } else if (options.gaugeType === 'verticalBar') {
944 if (options.neonGlowBrightness) { 1016 if (options.neonGlowBrightness) {
945 context.strokeStyle = neonColor; 1017 context.strokeStyle = neonColor;
@@ -953,6 +1025,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, @@ -953,6 +1025,8 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
953 baseX + width/2, barBottom - (barBottom-barTop)*progress, 1025 baseX + width/2, barBottom - (barBottom-barTop)*progress,
954 neonColor, strokeWidth, true); 1026 neonColor, strokeWidth, true);
955 } 1027 }
  1028 + drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth,
  1029 + true, options.colorTicks, options.tickWidth);
956 } 1030 }
957 1031
958 } 1032 }
@@ -19,25 +19,27 @@ import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; @@ -19,25 +19,27 @@ 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 { 22 +export interface AttributeSourceProperty {
23 valueSource: string; 23 valueSource: string;
24 entityAlias?: string; 24 entityAlias?: string;
25 attribute?: string; 25 attribute?: string;
26 value?: number; 26 value?: number;
27 } 27 }
28 28
29 -export interface fixedLevelColors {  
30 - from?: colorLevelProperty;  
31 - to?: colorLevelProperty; 29 +export interface FixedLevelColors {
  30 + from?: AttributeSourceProperty;
  31 + to?: AttributeSourceProperty;
32 color: string; 32 color: string;
33 } 33 }
34 34
35 -export interface colorLevelSetting { 35 +export interface ColorLevelSetting {
36 value: number; 36 value: number;
37 color: string; 37 color: string;
38 } 38 }
39 39
40 -export type colorLevel = Array<string | colorLevelSetting>; 40 +export type colorLevel = Array<string | ColorLevelSetting>;
  41 +
  42 +export type attributesGaugeType = 'levelColors' | 'ticks';
41 43
42 export interface DigitalGaugeSettings { 44 export interface DigitalGaugeSettings {
43 minValue?: number; 45 minValue?: number;
@@ -60,7 +62,7 @@ export interface DigitalGaugeSettings { @@ -60,7 +62,7 @@ export interface DigitalGaugeSettings {
60 gaugeColor?: string; 62 gaugeColor?: string;
61 useFixedLevelColor?: boolean; 63 useFixedLevelColor?: boolean;
62 levelColors?: colorLevel; 64 levelColors?: colorLevel;
63 - fixedLevelColors?: fixedLevelColors[]; 65 + fixedLevelColors?: FixedLevelColors[];
64 animation?: boolean; 66 animation?: boolean;
65 animationDuration?: number; 67 animationDuration?: number;
66 animationRule?: AnimationRule; 68 animationRule?: AnimationRule;
@@ -72,6 +74,11 @@ export interface DigitalGaugeSettings { @@ -72,6 +74,11 @@ export interface DigitalGaugeSettings {
72 units?: string; 74 units?: string;
73 hideValue?: boolean; 75 hideValue?: boolean;
74 hideMinMax?: boolean; 76 hideMinMax?: boolean;
  77 + showTicks?: boolean;
  78 + ticksValue?: AttributeSourceProperty[];
  79 + ticks?: number[];
  80 + colorTicks?: string;
  81 + tickWidth?: number;
75 } 82 }
76 83
77 export const digitalGaugeSettingsSchema: JsonSettingsSchema = { 84 export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
@@ -242,6 +249,48 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -242,6 +249,48 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
242 } 249 }
243 } 250 }
244 }, 251 },
  252 + showTicks: {
  253 + title: 'Show ticks',
  254 + type: 'boolean',
  255 + default: false
  256 + },
  257 + tickWidth: {
  258 + title: 'Width ticks',
  259 + type: 'number',
  260 + default: 4
  261 + },
  262 + colorTicks: {
  263 + title: 'Color ticks',
  264 + type: 'string',
  265 + default: '#666'
  266 + },
  267 + ticksValue: {
  268 + title: 'The ticks predefined value',
  269 + type: 'array',
  270 + items: {
  271 + title: 'tickValue',
  272 + type: 'object',
  273 + properties: {
  274 + valueSource: {
  275 + title: 'Value source',
  276 + type: 'string',
  277 + default: 'predefinedValue'
  278 + },
  279 + entityAlias: {
  280 + title: 'Source entity alias',
  281 + type: 'string'
  282 + },
  283 + attribute: {
  284 + title: 'Source entity attribute',
  285 + type: 'string'
  286 + },
  287 + value: {
  288 + title: 'Value (if predefined value is selected)',
  289 + type: 'number'
  290 + }
  291 + }
  292 + }
  293 + },
245 animation: { 294 animation: {
246 title: 'Enable animation', 295 title: 'Enable animation',
247 type: 'boolean', 296 type: 'boolean',
@@ -487,6 +536,40 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { @@ -487,6 +536,40 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
487 } 536 }
488 ] 537 ]
489 }, 538 },
  539 + 'showTicks',
  540 + {
  541 + key: 'tickWidth',
  542 + condition: 'model.showTicks === true'
  543 + },
  544 + {
  545 + key: 'colorTicks',
  546 + condition: 'model.showTicks === true',
  547 + type: 'color'
  548 + },
  549 + {
  550 + key: 'ticksValue',
  551 + condition: 'model.showTicks === true',
  552 + items: [
  553 + {
  554 + key: 'ticksValue[].valueSource',
  555 + type: 'rc-select',
  556 + multiple: false,
  557 + items: [
  558 + {
  559 + value: 'predefinedValue',
  560 + label: 'Predefined value (Default)'
  561 + },
  562 + {
  563 + value: 'entityAttribute',
  564 + label: 'Value taken from entity attribute'
  565 + }
  566 + ]
  567 + },
  568 + 'ticksValue[].value',
  569 + 'ticksValue[].entityAlias',
  570 + 'ticksValue[].attribute'
  571 + ]
  572 + },
490 'animation', 573 'animation',
491 'animationDuration', 574 'animationDuration',
492 { 575 {
@@ -17,19 +17,30 @@ @@ -17,19 +17,30 @@
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 { 19 import {
20 - colorLevelSetting, 20 + attributesGaugeType,
  21 + AttributeSourceProperty,
  22 + ColorLevelSetting,
21 DigitalGaugeSettings, 23 DigitalGaugeSettings,
22 - digitalGaugeSettingsSchema 24 + digitalGaugeSettingsSchema,
  25 + FixedLevelColors
23 } from '@home/components/widget/lib/digital-gauge.models'; 26 } from '@home/components/widget/lib/digital-gauge.models';
24 import * as tinycolor_ from 'tinycolor2'; 27 import * as tinycolor_ from 'tinycolor2';
25 import { isDefined } from '@core/utils'; 28 import { isDefined } from '@core/utils';
26 import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; 29 import { prepareFontSettings } from '@home/components/widget/lib/settings.models';
27 import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; 30 import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge';
28 import { DatePipe } from '@angular/common'; 31 import { DatePipe } from '@angular/common';
29 -import {DataKey, Datasource, DatasourceType, JsonSettingsSchema, widgetType} from '@shared/models/widget.models'; 32 +import {
  33 + DataKey,
  34 + Datasource,
  35 + DatasourceData,
  36 + DatasourceType,
  37 + JsonSettingsSchema,
  38 + widgetType
  39 +} from '@shared/models/widget.models';
  40 +import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
  41 +import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
  42 +import { EMPTY, Observable } from 'rxjs';
30 import GenericOptions = CanvasGauges.GenericOptions; 43 import GenericOptions = CanvasGauges.GenericOptions;
31 -import {IWidgetSubscription, WidgetSubscriptionOptions} from "@core/api/widget-api.models";  
32 -import {DataKeyType} from "@shared/models/telemetry/telemetry.models";  
33 44
34 const tinycolor = tinycolor_; 45 const tinycolor = tinycolor_;
35 46
@@ -37,11 +48,6 @@ const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema; @@ -37,11 +48,6 @@ const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema;
37 48
38 export class TbCanvasDigitalGauge { 49 export class TbCanvasDigitalGauge {
39 50
40 - private localSettings: DigitalGaugeSettings;  
41 - private levelColorsSourcesSubscription: IWidgetSubscription;  
42 -  
43 - private gauge: CanvasDigitalGauge;  
44 -  
45 static get settingsSchema(): JsonSettingsSchema { 51 static get settingsSchema(): JsonSettingsSchema {
46 return digitalGaugeSettingsSchemaValue; 52 return digitalGaugeSettingsSchemaValue;
47 } 53 }
@@ -84,6 +90,12 @@ export class TbCanvasDigitalGauge { @@ -84,6 +90,12 @@ export class TbCanvasDigitalGauge {
84 this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; 90 this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
85 } 91 }
86 92
  93 + this.localSettings.showTicks = settings.showTicks || false;
  94 + this.localSettings.ticks = [];
  95 + this.localSettings.ticksValue = settings.ticksValue || [];
  96 + this.localSettings.tickWidth = settings.tickWidth || 4;
  97 + this.localSettings.colorTicks = settings.colorTicks || '#666';
  98 +
87 this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals : 99 this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals :
88 ((isDefined(settings.decimals) && settings.decimals !== null) 100 ((isDefined(settings.decimals) && settings.decimals !== null)
89 ? settings.decimals : ctx.decimals); 101 ? settings.decimals : ctx.decimals);
@@ -137,6 +149,10 @@ export class TbCanvasDigitalGauge { @@ -137,6 +149,10 @@ export class TbCanvasDigitalGauge {
137 gaugeColor: this.localSettings.gaugeColor, 149 gaugeColor: this.localSettings.gaugeColor,
138 levelColors: this.localSettings.levelColors, 150 levelColors: this.localSettings.levelColors,
139 151
  152 + colorTicks: this.localSettings.colorTicks,
  153 + tickWidth: this.localSettings.tickWidth,
  154 + ticks: this.localSettings.ticks,
  155 +
140 title: this.localSettings.title, 156 title: this.localSettings.title,
141 157
142 fontTitleSize: this.localSettings.titleFont.size, 158 fontTitleSize: this.localSettings.titleFont.size,
@@ -192,76 +208,92 @@ export class TbCanvasDigitalGauge { @@ -192,76 +208,92 @@ export class TbCanvasDigitalGauge {
192 this.init(); 208 this.init();
193 } 209 }
194 210
  211 + private localSettings: DigitalGaugeSettings;
  212 + private levelColorsSourcesSubscription: IWidgetSubscription;
  213 + private ticksSourcesSubscription: IWidgetSubscription;
  214 +
  215 + private gauge: CanvasDigitalGauge;
  216 +
  217 + static generateDatasource(ctx: WidgetContext, datasources: Datasource[], entityAlias: string,
  218 + attribute: string, settings: any): Datasource[]{
  219 + const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias);
  220 + if (!entityAliasId) {
  221 + throw new Error('Not valid entity aliase name ' + entityAlias);
  222 + }
  223 +
  224 + const datasource = datasources.find((datasourceIteration) => {
  225 + return datasourceIteration.entityAliasId === entityAliasId;
  226 + });
  227 +
  228 + const dataKey: DataKey = {
  229 + type: DataKeyType.attribute,
  230 + name: attribute,
  231 + label: attribute,
  232 + settings: [settings],
  233 + _hash: Math.random()
  234 + };
  235 +
  236 + if (datasource) {
  237 + const findDataKey = datasource.dataKeys.find((dataKeyIteration) => {
  238 + return dataKeyIteration.name === attribute;
  239 + });
  240 +
  241 + if (findDataKey) {
  242 + findDataKey.settings.push(settings);
  243 + } else {
  244 + datasource.dataKeys.push(dataKey)
  245 + }
  246 + } else {
  247 + const datasourceAttribute: Datasource = {
  248 + type: DatasourceType.entity,
  249 + name: entityAlias,
  250 + aliasName: entityAlias,
  251 + entityAliasId,
  252 + dataKeys: [dataKey]
  253 + };
  254 + datasources.push(datasourceAttribute);
  255 + }
  256 +
  257 + return datasources;
  258 + }
  259 +
195 init() { 260 init() {
196 if (this.localSettings.useFixedLevelColor) { 261 if (this.localSettings.useFixedLevelColor) {
197 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { 262 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
198 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); 263 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
199 - this.updateLevelColors(this.localSettings.levelColors);  
200 } 264 }
  265 +
  266 + if (this.localSettings.showTicks) {
  267 + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) {
  268 + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue);
  269 + }
  270 + }
  271 + this.updateSetting();
201 } 272 }
202 } 273 }
203 274
204 - settingLevelColorsSubscribe(options) { 275 + settingLevelColorsSubscribe(options: FixedLevelColors[]): ColorLevelSetting[] {
205 let levelColorsDatasource: Datasource[] = []; 276 let levelColorsDatasource: Datasource[] = [];
206 - let predefineLevelColors: colorLevelSetting[] = []; 277 + const predefineLevelColors: ColorLevelSetting[] = [];
207 278
208 - function setLevelColor(levelSetting, color) { 279 + function setLevelColor(levelSetting: AttributeSourceProperty, color: string) {
209 if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) { 280 if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) {
210 predefineLevelColors.push({ 281 predefineLevelColors.push({
211 value: levelSetting.value, 282 value: levelSetting.value,
212 - color: color 283 + color
213 }) 284 })
214 } else if (levelSetting.entityAlias && levelSetting.attribute) { 285 } else if (levelSetting.entityAlias && levelSetting.attribute) {
215 - let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias);  
216 - if (!entityAliasId) { 286 + try {
  287 + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasource(this.ctx, levelColorsDatasource,
  288 + levelSetting.entityAlias, levelSetting.attribute, {color, index: predefineLevelColors.length});
  289 + } catch (e) {
217 return; 290 return;
218 } 291 }
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); 292 predefineLevelColors.push(null);
260 } 293 }
261 } 294 }
262 295
263 - for (let i = 0; i < options.length; i++) {  
264 - let levelColor = options[i]; 296 + for(const levelColor of options){
265 if (levelColor.from) { 297 if (levelColor.from) {
266 setLevelColor.call(this, levelColor.from, levelColor.color); 298 setLevelColor.call(this, levelColor.from, levelColor.color);
267 } 299 }
@@ -270,49 +302,86 @@ export class TbCanvasDigitalGauge { @@ -270,49 +302,86 @@ export class TbCanvasDigitalGauge {
270 } 302 }
271 } 303 }
272 304
273 - this.subscribeLevelColorsAttributes(levelColorsDatasource); 305 + this.subscribeAttributes(levelColorsDatasource, 'levelColors').subscribe((subscription) => {
  306 + this.levelColorsSourcesSubscription = subscription;
  307 + });
274 308
275 return predefineLevelColors; 309 return predefineLevelColors;
276 } 310 }
277 311
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); 312 + settingTicksSubscribe(options: AttributeSourceProperty[]): number[] {
  313 + let ticksDatasource: Datasource[] = [];
  314 + const predefineTicks: number[] = [];
  315 +
  316 + for(const tick of options){
  317 + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) {
  318 + predefineTicks.push(tick.value)
  319 + } else if (tick.entityAlias && tick.attribute) {
  320 + try {
  321 + ticksDatasource = TbCanvasDigitalGauge
  322 + .generateDatasource(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length);
  323 + } catch (e) {
  324 + continue;
  325 + }
  326 + predefineTicks.push(null);
  327 + }
  328 + }
  329 +
  330 + this.subscribeAttributes(ticksDatasource, 'ticks').subscribe((subscription) => {
  331 + this.ticksSourcesSubscription = subscription;
  332 + });
  333 +
  334 + return predefineTicks;
282 } 335 }
283 336
284 - subscribeLevelColorsAttributes(datasources: Datasource[]) {  
285 - let TbCanvasDigitalGauge = this;  
286 - let levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = {  
287 - datasources: datasources, 337 + subscribeAttributes(datasource: Datasource[], typeAttributes: attributesGaugeType): Observable<IWidgetSubscription> {
  338 + if (!datasource.length) {
  339 + return EMPTY;
  340 + }
  341 +
  342 + const levelColorsSourcesSubscriptionOptions: WidgetSubscriptionOptions = {
  343 + datasources: datasource,
288 useDashboardTimewindow: false, 344 useDashboardTimewindow: false,
289 type: widgetType.latest, 345 type: widgetType.latest,
290 callbacks: { 346 callbacks: {
291 onDataUpdated: (subscription) => { 347 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); 348 + this.updateAttribute(subscription.data, typeAttributes);
308 } 349 }
309 } 350 }
310 }; 351 };
311 - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).subscribe(  
312 - (subscription) => {  
313 - TbCanvasDigitalGauge.levelColorsSourcesSubscription = subscription; 352 +
  353 + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true);
  354 + }
  355 +
  356 + updateAttribute(data: Array<DatasourceData>, typeAttributes: attributesGaugeType) {
  357 + for (const keyData of data) {
  358 + if (keyData && keyData.data && keyData.data[0]) {
  359 + const attrValue = keyData.data[0][1];
  360 + if (isFinite(attrValue)) {
  361 + for (const setting of keyData.dataKey.settings) {
  362 + switch (typeAttributes) {
  363 + case 'levelColors':
  364 + this.localSettings.levelColors[setting.index] = {
  365 + value: attrValue,
  366 + color: setting.color
  367 + };
  368 + break;
  369 + case 'ticks':
  370 + this.localSettings.ticks[setting] = attrValue;
  371 + break;
  372 + }
  373 + }
  374 + }
314 } 375 }
315 - ); 376 + }
  377 + this.updateSetting();
  378 + }
  379 +
  380 + updateSetting() {
  381 + (this.gauge.options as CanvasDigitalGaugeOptions).ticks = this.localSettings.ticks;
  382 + (this.gauge.options as CanvasDigitalGaugeOptions).levelColors = this.localSettings.levelColors;
  383 + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
  384 + this.gauge.update({} as CanvasDigitalGaugeOptions);
316 } 385 }
317 386
318 update() { 387 update() {