Commit f57ed509fc480e94a4fa4dc649032f742317f278

Authored by Vladyslav
Committed by GitHub
2 parents e9e24f3a 2f8ea308

Merge pull request #2507 from vvlladd28/feature/digital-gauge/ticks

[2.5] 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,12 @@ 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.ticks = [];
  67 + this.localSettings.ticksValue = settings.ticksValue || [];
  68 + this.localSettings.tickWidth = settings.tickWidth || 4;
  69 + this.localSettings.colorTicks = settings.colorTicks || '#666';
  70 +
65 71 this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
66 72 ((angular.isDefined(settings.decimals) && settings.decimals !== null)
67 73 ? settings.decimals : ctx.decimals);
... ... @@ -145,6 +151,10 @@ export default class TbCanvasDigitalGauge {
145 151 gaugeColor: this.localSettings.gaugeColor,
146 152 levelColors: this.localSettings.levelColors,
147 153
  154 + colorTicks: this.localSettings.colorTicks,
  155 + tickWidth: this.localSettings.tickWidth,
  156 + ticks: this.localSettings.ticks,
  157 +
148 158 title: this.localSettings.title,
149 159
150 160 fontTitleSize: this.localSettings.titleFont.size,
... ... @@ -204,9 +214,81 @@ export default class TbCanvasDigitalGauge {
204 214 if (this.localSettings.useFixedLevelColor) {
205 215 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
206 216 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
207   - this.updateLevelColors(this.localSettings.levelColors);
208 217 }
209 218 }
  219 + if (this.localSettings.showTicks) {
  220 + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) {
  221 + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue);
  222 + }
  223 + }
  224 + this.updateSetting();
  225 + }
  226 +
  227 + static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){
  228 + let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias);
  229 + if (!entityAliasId) {
  230 + throw new Error('Not valid entity aliase name ' + entityAlias);
  231 + }
  232 +
  233 + let datasource = datasources.filter((datasource) => {
  234 + return datasource.entityAliasId === entityAliasId;
  235 + })[0];
  236 +
  237 + let dataKey = {
  238 + type: ctx.$scope.$injector.get('types').dataKeyType.attribute,
  239 + name: attribute,
  240 + label: attribute,
  241 + settings: [settings],
  242 + _hash: Math.random()
  243 + };
  244 +
  245 + if (datasource) {
  246 + let findDataKey = datasource.dataKeys.filter((dataKey) => {
  247 + return dataKey.name === attribute;
  248 + })[0];
  249 +
  250 + if (findDataKey) {
  251 + findDataKey.settings.push(settings);
  252 + } else {
  253 + datasource.dataKeys.push(dataKey)
  254 + }
  255 + } else {
  256 + datasource = {
  257 + type: ctx.$scope.$injector.get('types').datasourceType.entity,
  258 + name: entityAlias,
  259 + aliasName: entityAlias,
  260 + entityAliasId: entityAliasId,
  261 + dataKeys: [dataKey]
  262 + };
  263 + datasources.push(datasource);
  264 + }
  265 +
  266 + return datasources;
  267 + }
  268 +
  269 + settingTicksSubscribe(options) {
  270 + let ticksDatasource = [];
  271 + let predefineTicks = [];
  272 +
  273 + for (let i = 0; i < options.length; i++) {
  274 + let tick = options[i];
  275 + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) {
  276 + predefineTicks.push(tick.value)
  277 + } else if (tick.entityAlias && tick.attribute) {
  278 + try {
  279 + ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length);
  280 + } catch (e) {
  281 + continue;
  282 + }
  283 + predefineTicks.push(null);
  284 + }
  285 + }
  286 +
  287 + this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => {
  288 + this.ticksSourcesSubscription = subscription;
  289 + });
  290 +
  291 + return predefineTicks;
210 292 }
211 293
212 294 settingLevelColorsSubscribe(options) {
... ... @@ -220,50 +302,14 @@ export default class TbCanvasDigitalGauge {
220 302 color: color
221 303 })
222 304 } 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: [{
  305 + try {
  306 + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, {
237 307 color: color,
238 308 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);
  309 + });
  310 + } catch (e) {
  311 + return;
265 312 }
266   -
267 313 predefineLevelColors.push(null);
268 314 }
269 315 }
... ... @@ -278,49 +324,63 @@ export default class TbCanvasDigitalGauge {
278 324 }
279 325 }
280 326
281   - this.subscribeLevelColorsAttributes(levelColorsDatasource);
  327 + this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => {
  328 + this.levelColorSourcesSubscription = subscription;
  329 + });
282 330
283 331 return predefineLevelColors;
284 332 }
285 333
286   - updateLevelColors(levelColors) {
287   - this.gauge.options.levelColors = levelColors;
288   - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
289   - this.gauge.update();
290   - }
  334 + subscribeAttributes(datasources, typeAttributes) {
  335 + if (!datasources.length) {
  336 + return this.ctx.$scope.$injector.get('$q').when(null);
  337 + }
291 338
292   - subscribeLevelColorsAttributes(datasources) {
293   - let TbCanvasDigitalGauge = this;
294 339 let levelColorsSourcesSubscriptionOptions = {
295 340 datasources: datasources,
296 341 useDashboardTimewindow: false,
297 342 type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
298 343 callbacks: {
299 344 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);
  345 + this.updateAttribute(subscription.data, typeAttributes);
316 346 }
317 347 }
318 348 };
319   - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then(
320   - (subscription) => {
321   - TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription;
  349 +
  350 + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true);
  351 + }
  352 +
  353 + updateAttribute(data, typeAttributes) {
  354 + for (let i = 0; i < data.length; i++) {
  355 + let keyData = data[i];
  356 + if (keyData && keyData.data && keyData.data[0]) {
  357 + let attrValue = keyData.data[0][1];
  358 + if (isFinite(attrValue)) {
  359 + for (let i = 0; i < keyData.dataKey.settings.length; i++) {
  360 + let setting = keyData.dataKey.settings[i];
  361 + switch (typeAttributes) {
  362 + case 'levelColors':
  363 + this.localSettings.levelColors[setting.index] = {
  364 + value: attrValue,
  365 + color: setting.color
  366 + };
  367 + break;
  368 + case 'ticks':
  369 + this.localSettings.ticks[setting] = attrValue;
  370 + break;
  371 + }
  372 + }
  373 + }
322 374 }
323   - );
  375 + }
  376 + this.updateSetting();
  377 + }
  378 +
  379 + updateSetting() {
  380 + this.gauge.options.ticks = this.localSettings.ticks;
  381 + this.gauge.options.levelColors = this.localSettings.levelColors;
  382 + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
  383 + this.gauge.update();
324 384 }
325 385
326 386 update() {
... ... @@ -526,6 +586,48 @@ export default class TbCanvasDigitalGauge {
526 586 }
527 587 }
528 588 },
  589 + "showTicks": {
  590 + "title": "Show ticks",
  591 + "type": "boolean",
  592 + "default": false
  593 + },
  594 + "tickWidth": {
  595 + "title": "Width ticks",
  596 + "type": "number",
  597 + "default": 4
  598 + },
  599 + "colorTicks": {
  600 + "title": "Color ticks",
  601 + "type": "string",
  602 + "default": "#666"
  603 + },
  604 + "ticksValue": {
  605 + "title": "The ticks predefined value",
  606 + "type": "array",
  607 + "items": {
  608 + "title": "tickValue",
  609 + "type": "object",
  610 + "properties": {
  611 + "valueSource": {
  612 + "title": "Value source",
  613 + "type": "string",
  614 + "default": "predefinedValue"
  615 + },
  616 + "entityAlias": {
  617 + "title": "Source entity alias",
  618 + "type": "string"
  619 + },
  620 + "attribute": {
  621 + "title": "Source entity attribute",
  622 + "type": "string"
  623 + },
  624 + "value": {
  625 + "title": "Value (if predefined value is selected)",
  626 + "type": "number"
  627 + }
  628 + }
  629 + }
  630 + },
529 631 "animation": {
530 632 "title": "Enable animation",
531 633 "type": "boolean",
... ... @@ -771,6 +873,40 @@ export default class TbCanvasDigitalGauge {
771 873 }
772 874 ]
773 875 },
  876 + "showTicks",
  877 + {
  878 + "key": "tickWidth",
  879 + "condition": "model.showTicks === true"
  880 + },
  881 + {
  882 + "key": "colorTicks",
  883 + "condition": "model.showTicks === true",
  884 + "type": "color"
  885 + },
  886 + {
  887 + "key": "ticksValue",
  888 + "condition": "model.showTicks === true",
  889 + "items": [
  890 + {
  891 + "key": "ticksValue[].valueSource",
  892 + "type": "rc-select",
  893 + "multiple": false,
  894 + "items": [
  895 + {
  896 + "value": "predefinedValue",
  897 + "label": "Predefined value (Default)"
  898 + },
  899 + {
  900 + "value": "entityAttribute",
  901 + "label": "Value taken from entity attribute"
  902 + }
  903 + ]
  904 + },
  905 + "ticksValue[].value",
  906 + "ticksValue[].entityAlias",
  907 + "ticksValue[].attribute"
  908 + ]
  909 + },
774 910 "animation",
775 911 "animationDuration",
776 912 {
... ...