Commit 49be7d1ec3eac3a98dcc7cfd05498cbefcf9957e

Authored by Vladyslav_Prykhodko
1 parent f3a01f21

Add support ticks to digital gauge

@@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions, @@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions,
51 51
52 neonGlowBrightness: 0, 52 neonGlowBrightness: 0,
53 53
  54 + colorTicks: 'gray',
  55 + tickWidth: 4,
  56 + ticks: [],
  57 +
54 isMobile: false 58 isMobile: false
55 59
56 }); 60 });
@@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { @@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge {
133 } 137 }
134 } 138 }
135 139
  140 + options.ticksValue = [];
  141 + for(let i = 0; i < options.ticks.length; i++){
  142 + if(options.ticks[i] !== null){
  143 + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(options.ticks[i], options.minValue, options.maxValue))
  144 + }
  145 + }
  146 +
136 if (options.neonGlowBrightness) { 147 if (options.neonGlowBrightness) {
137 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString(); 148 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
138 options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString(); 149 options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
@@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is @@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is
729 context.stroke(); 740 context.stroke();
730 } 741 }
731 742
  743 +function drawTickArc(context, tickValues, Cx, Cy, Ri, Rm, Ro, startAngle, endAngle, color, tickWidth) {
  744 + if(!tickValues.length) {
  745 + return;
  746 + }
  747 +
  748 + const strokeWidth = Ro - Ri;
  749 + context.beginPath();
  750 + context.lineWidth = tickWidth;
  751 + context.strokeStyle = color;
  752 + for (let i = 0; i < tickValues.length; i++) {
  753 + var angle = startAngle + tickValues[i] * endAngle;
  754 + var x1 = Cx + (Ri + strokeWidth) * Math.cos(angle);
  755 + var y1 = Cy + (Ri + strokeWidth) * Math.sin(angle);
  756 + var x2 = Cx + Ri * Math.cos(angle);
  757 + var y2 = Cy + Ri * Math.sin(angle);
  758 + context.moveTo(x1, y1);
  759 + context.lineTo(x2, y2);
  760 + }
  761 + context.stroke();
  762 +}
  763 +
  764 +function drawTickBar(context, tickValues, startX, startY, distanceBar, strokeWidth, isVertical, color, tickWidth) {
  765 + if(!tickValues.length) {
  766 + return;
  767 + }
  768 +
  769 + context.beginPath();
  770 + context.lineWidth = tickWidth;
  771 + context.strokeStyle = color;
  772 + for (let i = 0; i < tickValues.length; i++) {
  773 + let tickValue = tickValues[i] * distanceBar;
  774 + if (isVertical) {
  775 + context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar);
  776 + context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar);
  777 + } else {
  778 + context.moveTo(startX + tickValue, startY);
  779 + context.lineTo(startX + tickValue, startY + strokeWidth);
  780 + }
  781 + }
  782 + context.stroke();
  783 +}
  784 +
732 function drawProgress(context, options, progress) { 785 function drawProgress(context, options, progress) {
733 var neonColor; 786 var neonColor;
734 if (options.neonGlowBrightness) { 787 if (options.neonGlowBrightness) {
@@ -759,6 +812,7 @@ function drawProgress(context, options, progress) { @@ -759,6 +812,7 @@ function drawProgress(context, options, progress) {
759 if (options.neonGlowBrightness && !options.isMobile) { 812 if (options.neonGlowBrightness && !options.isMobile) {
760 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle); 813 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle);
761 } 814 }
  815 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle, options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth);
762 } else if (options.gaugeType === 'arc') { 816 } else if (options.gaugeType === 'arc') {
763 if (options.neonGlowBrightness) { 817 if (options.neonGlowBrightness) {
764 context.strokeStyle = neonColor; 818 context.strokeStyle = neonColor;
@@ -769,6 +823,7 @@ function drawProgress(context, options, progress) { @@ -769,6 +823,7 @@ function drawProgress(context, options, progress) {
769 if (options.neonGlowBrightness && !options.isMobile) { 823 if (options.neonGlowBrightness && !options.isMobile) {
770 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false); 824 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
771 } 825 }
  826 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth);
772 } else if (options.gaugeType === 'horizontalBar') { 827 } else if (options.gaugeType === 'horizontalBar') {
773 if (options.neonGlowBrightness) { 828 if (options.neonGlowBrightness) {
774 context.strokeStyle = neonColor; 829 context.strokeStyle = neonColor;
@@ -781,6 +836,7 @@ function drawProgress(context, options, progress) { @@ -781,6 +836,7 @@ function drawProgress(context, options, progress) {
781 drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, 836 drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
782 neonColor, strokeWidth, false); 837 neonColor, strokeWidth, false);
783 } 838 }
  839 + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth);
784 } else if (options.gaugeType === 'verticalBar') { 840 } else if (options.gaugeType === 'verticalBar') {
785 if (options.neonGlowBrightness) { 841 if (options.neonGlowBrightness) {
786 context.strokeStyle = neonColor; 842 context.strokeStyle = neonColor;
@@ -793,6 +849,7 @@ function drawProgress(context, options, progress) { @@ -793,6 +849,7 @@ function drawProgress(context, options, progress) {
793 drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress, 849 drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress,
794 neonColor, strokeWidth, true); 850 neonColor, strokeWidth, true);
795 } 851 }
  852 + drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, true, options.colorTicks, options.tickWidth);
796 } 853 }
797 } 854 }
798 855
@@ -62,6 +62,11 @@ export default class TbCanvasDigitalGauge { @@ -62,6 +62,11 @@ export default class TbCanvasDigitalGauge {
62 this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; 62 this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
63 } 63 }
64 64
  65 + this.localSettings.showTicks = settings.showTicks || false;
  66 + this.localSettings.ticksValue = settings.ticksValue;
  67 + this.localSettings.tickWidth = settings.tickWidth || 4;
  68 + this.localSettings.colorTicks = settings.colorTicks || '#666';
  69 +
65 this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : 70 this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
66 ((angular.isDefined(settings.decimals) && settings.decimals !== null) 71 ((angular.isDefined(settings.decimals) && settings.decimals !== null)
67 ? settings.decimals : ctx.decimals); 72 ? settings.decimals : ctx.decimals);
@@ -145,6 +150,10 @@ export default class TbCanvasDigitalGauge { @@ -145,6 +150,10 @@ export default class TbCanvasDigitalGauge {
145 gaugeColor: this.localSettings.gaugeColor, 150 gaugeColor: this.localSettings.gaugeColor,
146 levelColors: this.localSettings.levelColors, 151 levelColors: this.localSettings.levelColors,
147 152
  153 + colorTicks: this.localSettings.colorTicks,
  154 + tickWidth: this.localSettings.tickWidth,
  155 + ticks: [],
  156 +
148 title: this.localSettings.title, 157 title: this.localSettings.title,
149 158
150 fontTitleSize: this.localSettings.titleFont.size, 159 fontTitleSize: this.localSettings.titleFont.size,
@@ -204,9 +213,81 @@ export default class TbCanvasDigitalGauge { @@ -204,9 +213,81 @@ export default class TbCanvasDigitalGauge {
204 if (this.localSettings.useFixedLevelColor) { 213 if (this.localSettings.useFixedLevelColor) {
205 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { 214 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
206 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); 215 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
207 - this.updateLevelColors(this.localSettings.levelColors);  
208 } 216 }
209 } 217 }
  218 + if (this.localSettings.showTicks) {
  219 + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) {
  220 + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue);
  221 + }
  222 + }
  223 + this.updateSetting();
  224 + }
  225 +
  226 + static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){
  227 + let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias);
  228 + if (!entityAliasId) {
  229 + throw new Error('Not valid entity aliase name ' + entityAlias);
  230 + }
  231 +
  232 + let datasource = datasources.filter((datasource) => {
  233 + return datasource.entityAliasId === entityAliasId;
  234 + })[0];
  235 +
  236 + let dataKey = {
  237 + type: ctx.$scope.$injector.get('types').dataKeyType.attribute,
  238 + name: attribute,
  239 + label: attribute,
  240 + settings: [settings],
  241 + _hash: Math.random()
  242 + };
  243 +
  244 + if (datasource) {
  245 + let findDataKey = datasource.dataKeys.filter((dataKey) => {
  246 + return dataKey.name === attribute;
  247 + })[0];
  248 +
  249 + if (findDataKey) {
  250 + findDataKey.settings.push(settings);
  251 + } else {
  252 + datasource.dataKeys.push(dataKey)
  253 + }
  254 + } else {
  255 + datasource = {
  256 + type: ctx.$scope.$injector.get('types').datasourceType.entity,
  257 + name: entityAlias,
  258 + aliasName: entityAlias,
  259 + entityAliasId: entityAliasId,
  260 + dataKeys: [dataKey]
  261 + };
  262 + datasources.push(datasource);
  263 + }
  264 +
  265 + return datasources;
  266 + }
  267 +
  268 + settingTicksSubscribe(options) {
  269 + let ticksDatasource = [];
  270 + let predefineTicks = [];
  271 +
  272 + for (let i = 0; i < options.length; i++) {
  273 + let tick = options[i];
  274 + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) {
  275 + predefineTicks.push(tick.value)
  276 + } else if (tick.entityAlias && tick.attribute) {
  277 + try {
  278 + ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length);
  279 + } catch (e) {
  280 + continue;
  281 + }
  282 + predefineTicks.push(null);
  283 + }
  284 + }
  285 +
  286 + this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => {
  287 + this.ticksSourcesSubscription = subscription;
  288 + });
  289 +
  290 + return predefineTicks;
210 } 291 }
211 292
212 settingLevelColorsSubscribe(options) { 293 settingLevelColorsSubscribe(options) {
@@ -220,50 +301,14 @@ export default class TbCanvasDigitalGauge { @@ -220,50 +301,14 @@ export default class TbCanvasDigitalGauge {
220 color: color 301 color: color
221 }) 302 })
222 } else if (levelSetting.entityAlias && levelSetting.attribute) { 303 } else if (levelSetting.entityAlias && levelSetting.attribute) {
223 - let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias);  
224 - if (!entityAliasId) {  
225 - return;  
226 - }  
227 -  
228 - let datasource = levelColorsDatasource.filter((datasource) => {  
229 - return datasource.entityAliasId === entityAliasId;  
230 - })[0];  
231 -  
232 - let dataKey = {  
233 - type: this.ctx.$scope.$injector.get('types').dataKeyType.attribute,  
234 - name: levelSetting.attribute,  
235 - label: levelSetting.attribute,  
236 - settings: [{ 304 + try {
  305 + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, {
237 color: color, 306 color: color,
238 index: predefineLevelColors.length 307 index: predefineLevelColors.length
239 - }],  
240 - _hash: Math.random()  
241 - };  
242 -  
243 - if (datasource) {  
244 - let findDataKey = datasource.dataKeys.filter((dataKey) => {  
245 - return dataKey.name === levelSetting.attribute;  
246 - })[0];  
247 -  
248 - if (findDataKey) {  
249 - findDataKey.settings.push({  
250 - color: color,  
251 - index: predefineLevelColors.length  
252 - });  
253 - } else {  
254 - datasource.dataKeys.push(dataKey)  
255 - }  
256 - } else {  
257 - datasource = {  
258 - type: this.ctx.$scope.$injector.get('types').datasourceType.entity,  
259 - name: levelSetting.entityAlias,  
260 - aliasName: levelSetting.entityAlias,  
261 - entityAliasId: entityAliasId,  
262 - dataKeys: [dataKey]  
263 - };  
264 - levelColorsDatasource.push(datasource); 308 + });
  309 + } catch (e) {
  310 + return;
265 } 311 }
266 -  
267 predefineLevelColors.push(null); 312 predefineLevelColors.push(null);
268 } 313 }
269 } 314 }
@@ -278,49 +323,63 @@ export default class TbCanvasDigitalGauge { @@ -278,49 +323,63 @@ export default class TbCanvasDigitalGauge {
278 } 323 }
279 } 324 }
280 325
281 - this.subscribeLevelColorsAttributes(levelColorsDatasource); 326 + this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => {
  327 + this.levelColorSourcesSubscription = subscription;
  328 + });
282 329
283 return predefineLevelColors; 330 return predefineLevelColors;
284 } 331 }
285 332
286 - updateLevelColors(levelColors) {  
287 - this.gauge.options.levelColors = levelColors;  
288 - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);  
289 - this.gauge.update();  
290 - } 333 + subscribeAttributes(datasources, typeAttributes) {
  334 + if (!datasources.length) {
  335 + return this.ctx.$scope.$injector.get('$q').when(null);
  336 + }
291 337
292 - subscribeLevelColorsAttributes(datasources) {  
293 - let TbCanvasDigitalGauge = this;  
294 let levelColorsSourcesSubscriptionOptions = { 338 let levelColorsSourcesSubscriptionOptions = {
295 datasources: datasources, 339 datasources: datasources,
296 useDashboardTimewindow: false, 340 useDashboardTimewindow: false,
297 type: this.ctx.$scope.$injector.get('types').widgetType.latest.value, 341 type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
298 callbacks: { 342 callbacks: {
299 onDataUpdated: (subscription) => { 343 onDataUpdated: (subscription) => {
300 - for (let i = 0; i < subscription.data.length; i++) {  
301 - let keyData = subscription.data[i];  
302 - if (keyData && keyData.data && keyData.data[0]) {  
303 - let attrValue = keyData.data[0][1];  
304 - if (isFinite(attrValue)) {  
305 - for (let i = 0; i < keyData.dataKey.settings.length; i++) {  
306 - let setting = keyData.dataKey.settings[i];  
307 - this.localSettings.levelColors[setting.index] = {  
308 - value: attrValue,  
309 - color: setting.color  
310 - };  
311 - }  
312 - }  
313 - }  
314 - }  
315 - this.updateLevelColors(this.localSettings.levelColors); 344 + this.updateAttribute(subscription.data, typeAttributes);
316 } 345 }
317 } 346 }
318 }; 347 };
319 - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then(  
320 - (subscription) => {  
321 - TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription; 348 +
  349 + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true);
  350 + }
  351 +
  352 + updateAttribute(data, typeAttributes) {
  353 + for (let i = 0; i < data.length; i++) {
  354 + let keyData = data[i];
  355 + if (keyData && keyData.data && keyData.data[0]) {
  356 + let attrValue = keyData.data[0][1];
  357 + if (isFinite(attrValue)) {
  358 + for (let i = 0; i < keyData.dataKey.settings.length; i++) {
  359 + let setting = keyData.dataKey.settings[i];
  360 + switch (typeAttributes) {
  361 + case 'levelColors':
  362 + this.localSettings.levelColors[setting.index] = {
  363 + value: attrValue,
  364 + color: setting.color
  365 + };
  366 + break;
  367 + case 'ticks':
  368 + this.localSettings.ticks[setting] = attrValue;
  369 + break;
  370 + }
  371 + }
  372 + }
322 } 373 }
323 - ); 374 + }
  375 + this.updateSetting();
  376 + }
  377 +
  378 + updateSetting() {
  379 + this.gauge.options.ticks = this.localSettings.ticks;
  380 + this.gauge.options.levelColors = this.localSettings.levelColors;
  381 + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
  382 + this.gauge.update();
324 } 383 }
325 384
326 update() { 385 update() {
@@ -526,6 +585,48 @@ export default class TbCanvasDigitalGauge { @@ -526,6 +585,48 @@ export default class TbCanvasDigitalGauge {
526 } 585 }
527 } 586 }
528 }, 587 },
  588 + "showTicks": {
  589 + "title": "Show ticks",
  590 + "type": "boolean",
  591 + "default": false
  592 + },
  593 + "tickWidth": {
  594 + "title": "Width ticks",
  595 + "type": "number",
  596 + "default": 4
  597 + },
  598 + "colorTicks": {
  599 + "title": "Color ticks",
  600 + "type": "string",
  601 + "default": "#666"
  602 + },
  603 + "ticksValue": {
  604 + "title": "The ticks predefined value",
  605 + "type": "array",
  606 + "items": {
  607 + "title": "tickValue",
  608 + "type": "object",
  609 + "properties": {
  610 + "valueSource": {
  611 + "title": "Value source",
  612 + "type": "string",
  613 + "default": "predefinedValue"
  614 + },
  615 + "entityAlias": {
  616 + "title": "Source entity alias",
  617 + "type": "string"
  618 + },
  619 + "attribute": {
  620 + "title": "Source entity attribute",
  621 + "type": "string"
  622 + },
  623 + "value": {
  624 + "title": "Value (if predefined value is selected)",
  625 + "type": "number"
  626 + }
  627 + }
  628 + }
  629 + },
529 "animation": { 630 "animation": {
530 "title": "Enable animation", 631 "title": "Enable animation",
531 "type": "boolean", 632 "type": "boolean",
@@ -771,6 +872,40 @@ export default class TbCanvasDigitalGauge { @@ -771,6 +872,40 @@ export default class TbCanvasDigitalGauge {
771 } 872 }
772 ] 873 ]
773 }, 874 },
  875 + "showTicks",
  876 + {
  877 + "key": "tickWidth",
  878 + "condition": "model.showTicks === true"
  879 + },
  880 + {
  881 + "key": "colorTicks",
  882 + "condition": "model.showTicks === true",
  883 + "type": "color"
  884 + },
  885 + {
  886 + "key": "ticksValue",
  887 + "condition": "model.showTicks === true",
  888 + "items": [
  889 + {
  890 + "key": "ticksValue[].valueSource",
  891 + "type": "rc-select",
  892 + "multiple": false,
  893 + "items": [
  894 + {
  895 + "value": "predefinedValue",
  896 + "label": "Predefined value (Default)"
  897 + },
  898 + {
  899 + "value": "entityAttribute",
  900 + "label": "Value taken from entity attribute"
  901 + }
  902 + ]
  903 + },
  904 + "ticksValue[].value",
  905 + "ticksValue[].entityAlias",
  906 + "ticksValue[].attribute"
  907 + ]
  908 + },
774 "animation", 909 "animation",
775 "animationDuration", 910 "animationDuration",
776 { 911 {