Commit 37173bba100fe733911cfe19e9883aa8b4a6f981

Authored by Chantsova Ekaterina
Committed by GitHub
1 parent 01108b89

Features/flot thresholds (#2412)

* Thresholds draft

* Make generated thresholds unique

* Code refactoring
... ... @@ -33,7 +33,8 @@ export default class TbFlot {
33 33 this.ctx = ctx;
34 34 this.chartType = chartType || 'line';
35 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 39 ctx.tooltip = $('#flot-series-tooltip');
39 40 if (ctx.tooltip.length === 0) {
... ... @@ -242,7 +243,8 @@ export default class TbFlot {
242 243 grid: {
243 244 hoverable: true,
244 245 mouseActiveRadius: 10,
245   - autoHighlight: ctx.tooltipIndividual === true
  246 + autoHighlight: ctx.tooltipIndividual === true,
  247 + markings: []
246 248 },
247 249 selection : { mode : ctx.isMobile ? null : 'x' },
248 250 legend : {
... ... @@ -264,7 +266,7 @@ export default class TbFlot {
264 266 };
265 267 if (settings.xaxis) {
266 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 270 this.xaxis.labelFont.color = this.xaxis.font.color;
269 271 this.xaxis.labelFont.size = this.xaxis.font.size+2;
270 272 this.xaxis.labelFont.weight = "bold";
... ... @@ -301,7 +303,7 @@ export default class TbFlot {
301 303 this.yaxis.font.color = settings.yaxis.color || this.yaxis.font.color;
302 304 this.yaxis.min = angular.isDefined(settings.yaxis.min) ? settings.yaxis.min : null;
303 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 307 this.yaxis.labelFont.color = this.yaxis.font.color;
306 308 this.yaxis.labelFont.size = this.yaxis.font.size+2;
307 309 this.yaxis.labelFont.weight = "bold";
... ... @@ -364,7 +366,7 @@ export default class TbFlot {
364 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 370 xaxis.position = settings.xaxisSecond.axisPosition;
369 371 }
370 372 xaxis.tickLength = 0;
... ... @@ -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 399 if (this.chartType === 'bar') {
394 400 options.series.lines = {
395 401 show: false,
... ... @@ -471,6 +477,7 @@ export default class TbFlot {
471 477 var colors = [];
472 478 this.yaxes = [];
473 479 var yaxesMap = {};
  480 + let predefinedThresholds = [], thresholdsDatasources = [];
474 481
475 482 var tooltipValueFormatFunction = null;
476 483 if (this.ctx.settings.tooltipValueFormatter && this.ctx.settings.tooltipValueFormatter.length) {
... ... @@ -485,6 +492,7 @@ export default class TbFlot {
485 492 var series = this.subscription.data[i];
486 493 colors.push(series.dataKey.color);
487 494 var keySettings = series.dataKey.settings;
  495 +
488 496 series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction;
489 497 if (keySettings.tooltipValueFormatter && keySettings.tooltipValueFormatter.length) {
490 498 try {
... ... @@ -570,9 +578,58 @@ export default class TbFlot {
570 578 series.yaxis = series.yaxisIndex+1;
571 579 yaxis.keysInfo[i] = {hidden: false};
572 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 633 this.options.colors = colors;
577 634 this.options.yaxes = angular.copy(this.yaxes);
578 635 if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') {
... ... @@ -657,6 +714,74 @@ export default class TbFlot {
657 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 785 update() {
661 786 if (this.updateTimeoutHandle) {
662 787 this.ctx.$scope.$timeout.cancel(this.updateTimeoutHandle);
... ... @@ -982,6 +1107,12 @@ export default class TbFlot {
982 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 1116 properties["shadowSize"] = {
986 1117 "title": "Shadow size",
987 1118 "type": "number",
... ... @@ -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 1288 schema["form"].push("shadowSize");
1155 1289 schema["form"].push({
1156 1290 "key": "fontColor",
... ... @@ -1456,7 +1590,73 @@ export default class TbFlot {
1456 1590 };
1457 1591
1458 1592 var properties = schema["schema"]["properties"];
  1593 +
1459 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 1660 properties["comparisonSettings"] = {
1461 1661 "title": "Comparison Settings",
1462 1662 "type": "object",
... ...