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 51
52 52 neonGlowBrightness: 0,
53 53
  54 + colorTicks: 'gray',
  55 + tickWidth: 4,
  56 + ticks: [],
  57 +
54 58 isMobile: false
55 59
56 60 });
... ... @@ -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 147 if (options.neonGlowBrightness) {
137 148 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
138 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 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 785 function drawProgress(context, options, progress) {
733 786 var neonColor;
734 787 if (options.neonGlowBrightness) {
... ... @@ -759,6 +812,7 @@ function drawProgress(context, options, progress) {
759 812 if (options.neonGlowBrightness && !options.isMobile) {
760 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 816 } else if (options.gaugeType === 'arc') {
763 817 if (options.neonGlowBrightness) {
764 818 context.strokeStyle = neonColor;
... ... @@ -769,6 +823,7 @@ function drawProgress(context, options, progress) {
769 823 if (options.neonGlowBrightness && !options.isMobile) {
770 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 827 } else if (options.gaugeType === 'horizontalBar') {
773 828 if (options.neonGlowBrightness) {
774 829 context.strokeStyle = neonColor;
... ... @@ -781,6 +836,7 @@ function drawProgress(context, options, progress) {
781 836 drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
782 837 neonColor, strokeWidth, false);
783 838 }
  839 + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth);
784 840 } else if (options.gaugeType === 'verticalBar') {
785 841 if (options.neonGlowBrightness) {
786 842 context.strokeStyle = neonColor;
... ... @@ -793,6 +849,7 @@ function drawProgress(context, options, progress) {
793 849 drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress,
794 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 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 70 this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
66 71 ((angular.isDefined(settings.decimals) && settings.decimals !== null)
67 72 ? settings.decimals : ctx.decimals);
... ... @@ -145,6 +150,10 @@ export default class TbCanvasDigitalGauge {
145 150 gaugeColor: this.localSettings.gaugeColor,
146 151 levelColors: this.localSettings.levelColors,
147 152
  153 + colorTicks: this.localSettings.colorTicks,
  154 + tickWidth: this.localSettings.tickWidth,
  155 + ticks: [],
  156 +
148 157 title: this.localSettings.title,
149 158
150 159 fontTitleSize: this.localSettings.titleFont.size,
... ... @@ -204,9 +213,81 @@ export default class TbCanvasDigitalGauge {
204 213 if (this.localSettings.useFixedLevelColor) {
205 214 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
206 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 293 settingLevelColorsSubscribe(options) {
... ... @@ -220,50 +301,14 @@ export default class TbCanvasDigitalGauge {
220 301 color: color
221 302 })
222 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 306 color: color,
238 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 312 predefineLevelColors.push(null);
268 313 }
269 314 }
... ... @@ -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 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 338 let levelColorsSourcesSubscriptionOptions = {
295 339 datasources: datasources,
296 340 useDashboardTimewindow: false,
297 341 type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
298 342 callbacks: {
299 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 385 update() {
... ... @@ -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 630 "animation": {
530 631 "title": "Enable animation",
531 632 "type": "boolean",
... ... @@ -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 909 "animation",
775 910 "animationDuration",
776 911 {
... ...