Commit 37173bba100fe733911cfe19e9883aa8b4a6f981
Committed by
GitHub
1 parent
01108b89
Features/flot thresholds (#2412)
* Thresholds draft * Make generated thresholds unique * Code refactoring
Showing
1 changed file
with
205 additions
and
5 deletions
@@ -33,7 +33,8 @@ export default class TbFlot { | @@ -33,7 +33,8 @@ export default class TbFlot { | ||
33 | this.ctx = ctx; | 33 | this.ctx = ctx; |
34 | this.chartType = chartType || 'line'; | 34 | this.chartType = chartType || 'line'; |
35 | var settings = ctx.settings; | 35 | var settings = ctx.settings; |
36 | - var utils = this.ctx.$scope.$injector.get('utils'); | 36 | + this.utils = this.ctx.$scope.$injector.get('utils'); |
37 | + this.types = this.ctx.$scope.$injector.get('types'); | ||
37 | 38 | ||
38 | ctx.tooltip = $('#flot-series-tooltip'); | 39 | ctx.tooltip = $('#flot-series-tooltip'); |
39 | if (ctx.tooltip.length === 0) { | 40 | if (ctx.tooltip.length === 0) { |
@@ -242,7 +243,8 @@ export default class TbFlot { | @@ -242,7 +243,8 @@ export default class TbFlot { | ||
242 | grid: { | 243 | grid: { |
243 | hoverable: true, | 244 | hoverable: true, |
244 | mouseActiveRadius: 10, | 245 | mouseActiveRadius: 10, |
245 | - autoHighlight: ctx.tooltipIndividual === true | 246 | + autoHighlight: ctx.tooltipIndividual === true, |
247 | + markings: [] | ||
246 | }, | 248 | }, |
247 | selection : { mode : ctx.isMobile ? null : 'x' }, | 249 | selection : { mode : ctx.isMobile ? null : 'x' }, |
248 | legend : { | 250 | legend : { |
@@ -264,7 +266,7 @@ export default class TbFlot { | @@ -264,7 +266,7 @@ export default class TbFlot { | ||
264 | }; | 266 | }; |
265 | if (settings.xaxis) { | 267 | if (settings.xaxis) { |
266 | this.xaxis.font.color = settings.xaxis.color || this.xaxis.font.color; | 268 | this.xaxis.font.color = settings.xaxis.color || this.xaxis.font.color; |
267 | - this.xaxis.label = utils.customTranslation(settings.xaxis.title, settings.xaxis.title) || null; | 269 | + this.xaxis.label = this.utils.customTranslation(settings.xaxis.title, settings.xaxis.title) || null; |
268 | this.xaxis.labelFont.color = this.xaxis.font.color; | 270 | this.xaxis.labelFont.color = this.xaxis.font.color; |
269 | this.xaxis.labelFont.size = this.xaxis.font.size+2; | 271 | this.xaxis.labelFont.size = this.xaxis.font.size+2; |
270 | this.xaxis.labelFont.weight = "bold"; | 272 | this.xaxis.labelFont.weight = "bold"; |
@@ -301,7 +303,7 @@ export default class TbFlot { | @@ -301,7 +303,7 @@ export default class TbFlot { | ||
301 | this.yaxis.font.color = settings.yaxis.color || this.yaxis.font.color; | 303 | this.yaxis.font.color = settings.yaxis.color || this.yaxis.font.color; |
302 | this.yaxis.min = angular.isDefined(settings.yaxis.min) ? settings.yaxis.min : null; | 304 | this.yaxis.min = angular.isDefined(settings.yaxis.min) ? settings.yaxis.min : null; |
303 | this.yaxis.max = angular.isDefined(settings.yaxis.max) ? settings.yaxis.max : null; | 305 | this.yaxis.max = angular.isDefined(settings.yaxis.max) ? settings.yaxis.max : null; |
304 | - this.yaxis.label = utils.customTranslation(settings.yaxis.title, settings.yaxis.title) || null; | 306 | + this.yaxis.label = this.utils.customTranslation(settings.yaxis.title, settings.yaxis.title) || null; |
305 | this.yaxis.labelFont.color = this.yaxis.font.color; | 307 | this.yaxis.labelFont.color = this.yaxis.font.color; |
306 | this.yaxis.labelFont.size = this.yaxis.font.size+2; | 308 | this.yaxis.labelFont.size = this.yaxis.font.size+2; |
307 | this.yaxis.labelFont.weight = "bold"; | 309 | this.yaxis.labelFont.weight = "bold"; |
@@ -364,7 +366,7 @@ export default class TbFlot { | @@ -364,7 +366,7 @@ export default class TbFlot { | ||
364 | return ''; | 366 | return ''; |
365 | }; | 367 | }; |
366 | } | 368 | } |
367 | - xaxis.label = utils.customTranslation(settings.xaxisSecond.title, settings.xaxisSecond.title) || null; | 369 | + xaxis.label = this.utils.customTranslation(settings.xaxisSecond.title, settings.xaxisSecond.title) || null; |
368 | xaxis.position = settings.xaxisSecond.axisPosition; | 370 | xaxis.position = settings.xaxisSecond.axisPosition; |
369 | } | 371 | } |
370 | xaxis.tickLength = 0; | 372 | xaxis.tickLength = 0; |
@@ -390,6 +392,10 @@ export default class TbFlot { | @@ -390,6 +392,10 @@ export default class TbFlot { | ||
390 | } | 392 | } |
391 | } | 393 | } |
392 | 394 | ||
395 | + if (this.chartType === 'line' && isFinite(settings.thresholdsLineWidth)) { | ||
396 | + options.grid.markingsLineWidth = settings.thresholdsLineWidth; | ||
397 | + } | ||
398 | + | ||
393 | if (this.chartType === 'bar') { | 399 | if (this.chartType === 'bar') { |
394 | options.series.lines = { | 400 | options.series.lines = { |
395 | show: false, | 401 | show: false, |
@@ -471,6 +477,7 @@ export default class TbFlot { | @@ -471,6 +477,7 @@ export default class TbFlot { | ||
471 | var colors = []; | 477 | var colors = []; |
472 | this.yaxes = []; | 478 | this.yaxes = []; |
473 | var yaxesMap = {}; | 479 | var yaxesMap = {}; |
480 | + let predefinedThresholds = [], thresholdsDatasources = []; | ||
474 | 481 | ||
475 | var tooltipValueFormatFunction = null; | 482 | var tooltipValueFormatFunction = null; |
476 | if (this.ctx.settings.tooltipValueFormatter && this.ctx.settings.tooltipValueFormatter.length) { | 483 | if (this.ctx.settings.tooltipValueFormatter && this.ctx.settings.tooltipValueFormatter.length) { |
@@ -485,6 +492,7 @@ export default class TbFlot { | @@ -485,6 +492,7 @@ export default class TbFlot { | ||
485 | var series = this.subscription.data[i]; | 492 | var series = this.subscription.data[i]; |
486 | colors.push(series.dataKey.color); | 493 | colors.push(series.dataKey.color); |
487 | var keySettings = series.dataKey.settings; | 494 | var keySettings = series.dataKey.settings; |
495 | + | ||
488 | series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction; | 496 | series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction; |
489 | if (keySettings.tooltipValueFormatter && keySettings.tooltipValueFormatter.length) { | 497 | if (keySettings.tooltipValueFormatter && keySettings.tooltipValueFormatter.length) { |
490 | try { | 498 | try { |
@@ -570,9 +578,58 @@ export default class TbFlot { | @@ -570,9 +578,58 @@ export default class TbFlot { | ||
570 | series.yaxis = series.yaxisIndex+1; | 578 | series.yaxis = series.yaxisIndex+1; |
571 | yaxis.keysInfo[i] = {hidden: false}; | 579 | yaxis.keysInfo[i] = {hidden: false}; |
572 | yaxis.show = true; | 580 | yaxis.show = true; |
581 | + | ||
582 | + if (keySettings.thresholds && keySettings.thresholds.length) { | ||
583 | + for (let j = 0; j < keySettings.thresholds.length; j++) { | ||
584 | + let threshold = keySettings.thresholds[j]; | ||
585 | + if (threshold.thresholdValueSource === 'predefinedValue' && isFinite(threshold.thresholdValue)) { | ||
586 | + let colorIndex = this.subscription.data.length + predefinedThresholds.length; | ||
587 | + this.generateThreshold(predefinedThresholds, series.yaxis, threshold.lineWidth, threshold.color, colorIndex, threshold.thresholdValue); | ||
588 | + } else if (threshold.thresholdEntityAlias && threshold.thresholdAttribute) { | ||
589 | + let entityAliasId = this.ctx.aliasController.getEntityAliasId(threshold.thresholdEntityAlias); | ||
590 | + if (!entityAliasId) { | ||
591 | + continue; | ||
592 | + } | ||
593 | + | ||
594 | + let datasource = thresholdsDatasources.filter((datasource) => { | ||
595 | + return datasource.entityAliasId === entityAliasId; | ||
596 | + })[0]; | ||
597 | + | ||
598 | + let dataKey = { | ||
599 | + type: this.types.dataKeyType.attribute, | ||
600 | + name: threshold.thresholdAttribute, | ||
601 | + label: threshold.thresholdAttribute, | ||
602 | + settings: { | ||
603 | + yaxis: series.yaxis, | ||
604 | + lineWidth: threshold.lineWidth, | ||
605 | + color: threshold.color | ||
606 | + }, | ||
607 | + _hash: Math.random() | ||
608 | + }; | ||
609 | + | ||
610 | + if (datasource) { | ||
611 | + datasource.dataKeys.push(dataKey); | ||
612 | + } else { | ||
613 | + datasource = { | ||
614 | + type: this.types.datasourceType.entity, | ||
615 | + name: threshold.thresholdEntityAlias, | ||
616 | + aliasName: threshold.thresholdEntityAlias, | ||
617 | + entityAliasId: entityAliasId, | ||
618 | + dataKeys: [ dataKey ] | ||
619 | + }; | ||
620 | + thresholdsDatasources.push(datasource); | ||
621 | + } | ||
622 | + } | ||
623 | + } | ||
624 | + } | ||
573 | } | 625 | } |
574 | } | 626 | } |
575 | 627 | ||
628 | + this.subscribeForThresholdsAttributes(thresholdsDatasources); | ||
629 | + | ||
630 | + this.options.grid.markings = predefinedThresholds; | ||
631 | + this.predefinedThresholds = predefinedThresholds; | ||
632 | + | ||
576 | this.options.colors = colors; | 633 | this.options.colors = colors; |
577 | this.options.yaxes = angular.copy(this.yaxes); | 634 | this.options.yaxes = angular.copy(this.yaxes); |
578 | if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { | 635 | if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { |
@@ -657,6 +714,74 @@ export default class TbFlot { | @@ -657,6 +714,74 @@ export default class TbFlot { | ||
657 | return yaxis; | 714 | return yaxis; |
658 | } | 715 | } |
659 | 716 | ||
717 | + subscribeForThresholdsAttributes(datasources) { | ||
718 | + let tbFlot = this; | ||
719 | + let thresholdsSourcesSubscriptionOptions = { | ||
720 | + datasources: datasources, | ||
721 | + useDashboardTimewindow: false, | ||
722 | + type: this.types.widgetType.latest.value, | ||
723 | + callbacks: { | ||
724 | + onDataUpdated: (subscription) => {tbFlot.thresholdsSourcesDataUpdated(subscription.data)} | ||
725 | + } | ||
726 | + }; | ||
727 | + this.ctx.subscriptionApi.createSubscription(thresholdsSourcesSubscriptionOptions, true).then( | ||
728 | + (subscription) => { | ||
729 | + tbFlot.thresholdsSourcesSubscription = subscription; | ||
730 | + } | ||
731 | + ); | ||
732 | + } | ||
733 | + | ||
734 | + thresholdsSourcesDataUpdated(data) { | ||
735 | + let allThresholds = angular.copy(this.predefinedThresholds); | ||
736 | + for (let i = 0; i < data.length; i++) { | ||
737 | + let keyData = data[i]; | ||
738 | + if (keyData && keyData.data && keyData.data[0]) { | ||
739 | + let attrValue = keyData.data[0][1]; | ||
740 | + if (isFinite(attrValue)) { | ||
741 | + let settings = keyData.dataKey.settings; | ||
742 | + let colorIndex = this.subscription.data.length + allThresholds.length; | ||
743 | + this.generateThreshold(allThresholds, settings.yaxis, settings.lineWidth, settings.color, colorIndex, attrValue); | ||
744 | + } | ||
745 | + } | ||
746 | + } | ||
747 | + | ||
748 | + this.options.grid.markings = allThresholds; | ||
749 | + this.redrawPlot(); | ||
750 | + } | ||
751 | + | ||
752 | + generateThreshold(existingThresholds, yaxis, lineWidth, color, defaultColorIndex, thresholdValue) { | ||
753 | + let marking = {}; | ||
754 | + let markingYAxis; | ||
755 | + | ||
756 | + if (yaxis !== 1) { | ||
757 | + markingYAxis = 'y' + yaxis + 'axis'; | ||
758 | + } else { | ||
759 | + markingYAxis = 'yaxis'; | ||
760 | + } | ||
761 | + | ||
762 | + if (isFinite(lineWidth)) { | ||
763 | + marking.lineWidth = lineWidth; | ||
764 | + } | ||
765 | + | ||
766 | + if (angular.isDefined(color)) { | ||
767 | + marking.color = color; | ||
768 | + } else { | ||
769 | + marking.color = this.utils.getMaterialColor(defaultColorIndex); | ||
770 | + } | ||
771 | + | ||
772 | + marking[markingYAxis] = { | ||
773 | + from: thresholdValue, | ||
774 | + to: thresholdValue | ||
775 | + }; | ||
776 | + | ||
777 | + let similarMarkings = existingThresholds.filter((existingMarking) => { | ||
778 | + return angular.equals(existingMarking[markingYAxis], marking[markingYAxis]); | ||
779 | + }); | ||
780 | + if (!similarMarkings.length) { | ||
781 | + existingThresholds.push(marking); | ||
782 | + } | ||
783 | + } | ||
784 | + | ||
660 | update() { | 785 | update() { |
661 | if (this.updateTimeoutHandle) { | 786 | if (this.updateTimeoutHandle) { |
662 | this.ctx.$scope.$timeout.cancel(this.updateTimeoutHandle); | 787 | this.ctx.$scope.$timeout.cancel(this.updateTimeoutHandle); |
@@ -982,6 +1107,12 @@ export default class TbFlot { | @@ -982,6 +1107,12 @@ export default class TbFlot { | ||
982 | "default": "left" | 1107 | "default": "left" |
983 | }; | 1108 | }; |
984 | } | 1109 | } |
1110 | + if (chartType === 'graph' || chartType === 'bar') { | ||
1111 | + properties["thresholdsLineWidth"] = { | ||
1112 | + "title": "Default line width for all thresholds", | ||
1113 | + "type": "number" | ||
1114 | + }; | ||
1115 | + } | ||
985 | properties["shadowSize"] = { | 1116 | properties["shadowSize"] = { |
986 | "title": "Shadow size", | 1117 | "title": "Shadow size", |
987 | "type": "number", | 1118 | "type": "number", |
@@ -1151,6 +1282,9 @@ export default class TbFlot { | @@ -1151,6 +1282,9 @@ export default class TbFlot { | ||
1151 | ] | 1282 | ] |
1152 | }); | 1283 | }); |
1153 | } | 1284 | } |
1285 | + if (chartType === 'graph' || chartType === 'bar') { | ||
1286 | + schema["form"].push("thresholdsLineWidth"); | ||
1287 | + } | ||
1154 | schema["form"].push("shadowSize"); | 1288 | schema["form"].push("shadowSize"); |
1155 | schema["form"].push({ | 1289 | schema["form"].push({ |
1156 | "key": "fontColor", | 1290 | "key": "fontColor", |
@@ -1456,7 +1590,73 @@ export default class TbFlot { | @@ -1456,7 +1590,73 @@ export default class TbFlot { | ||
1456 | }; | 1590 | }; |
1457 | 1591 | ||
1458 | var properties = schema["schema"]["properties"]; | 1592 | var properties = schema["schema"]["properties"]; |
1593 | + | ||
1459 | if (chartType === 'graph' || chartType === 'bar') { | 1594 | if (chartType === 'graph' || chartType === 'bar') { |
1595 | + properties["thresholds"] = { | ||
1596 | + "title": "Thresholds", | ||
1597 | + "type": "array", | ||
1598 | + "items": { | ||
1599 | + "title": "Threshold", | ||
1600 | + "type": "object", | ||
1601 | + "properties": { | ||
1602 | + "thresholdValueSource": { | ||
1603 | + "title": "Threshold value source", | ||
1604 | + "type": "string", | ||
1605 | + "default": "predefinedValue" | ||
1606 | + }, | ||
1607 | + "thresholdEntityAlias": { | ||
1608 | + "title": "Thresholds source entity alias", | ||
1609 | + "type": "string" | ||
1610 | + }, | ||
1611 | + "thresholdAttribute": { | ||
1612 | + "title": "Threshold source entity attribute", | ||
1613 | + "type": "string" | ||
1614 | + }, | ||
1615 | + "thresholdValue": { | ||
1616 | + "title": "Threshold value (if predefined value is selected)", | ||
1617 | + "type": "number" | ||
1618 | + }, | ||
1619 | + "lineWidth": { | ||
1620 | + "title": "Line width", | ||
1621 | + "type": "number" | ||
1622 | + }, | ||
1623 | + "color": { | ||
1624 | + "title": "Color", | ||
1625 | + "type": "string" | ||
1626 | + } | ||
1627 | + } | ||
1628 | + }, | ||
1629 | + "required": [] | ||
1630 | + }; | ||
1631 | + schema["form"].push({ | ||
1632 | + "key": "thresholds", | ||
1633 | + "items": [ | ||
1634 | + { | ||
1635 | + "key": "thresholds[].thresholdValueSource", | ||
1636 | + "type": "rc-select", | ||
1637 | + "multiple": false, | ||
1638 | + "items": [ | ||
1639 | + { | ||
1640 | + "value": "predefinedValue", | ||
1641 | + "label": "Predefined value (Default)" | ||
1642 | + }, | ||
1643 | + { | ||
1644 | + "value": "entityAttribute", | ||
1645 | + "label": "Value taken from entity attribute" | ||
1646 | + } | ||
1647 | + ] | ||
1648 | + }, | ||
1649 | + "thresholds[].thresholdValue", | ||
1650 | + "thresholds[].thresholdEntityAlias", | ||
1651 | + "thresholds[].thresholdAttribute", | ||
1652 | + { | ||
1653 | + "key": "thresholds[].color", | ||
1654 | + "type": "color" | ||
1655 | + }, | ||
1656 | + "thresholds[].lineWidth" | ||
1657 | + ] | ||
1658 | + }); | ||
1659 | + | ||
1460 | properties["comparisonSettings"] = { | 1660 | properties["comparisonSettings"] = { |
1461 | "title": "Comparison Settings", | 1661 | "title": "Comparison Settings", |
1462 | "type": "object", | 1662 | "type": "object", |