@cryptotaxi247 / netdata-1 / commits / 59560da79

refactors units scaling; support temperature conversions; #2745

Costa Tsaousis (ktsaou) committed Oct 7, 2017 at 23:44 UTC 59560da79adb899831fbc7f4d4f46cb359599242
2 files changed +198 -111
web/dashboard.js
+159 -101
@@ -295,6 +295,7 @@ var NETDATA = window.NETDATA || {};
295 // we may have many...
296 current: {
297 units: 'auto', // can be 'auto' or 'original'
298 + temperature: 'celsius', // can be 'celsius' or 'fahrenheit'
299
300 pixels_per_point: isSlowDevice()?5:1, // the minimum pixels per point for all charts
301 // increase this to speed javascript up
@@ -1470,23 +1471,24 @@ var NETDATA = window.NETDATA || {};
1471 // units conversion
1472
1473 NETDATA.unitsConversion = {
1473 - keys: {},
1474 - latest: {},
1475 - units: {
1474 + keys: {}, // keys for data-common-units
1475 + latest: {}, // latest selected units for data-common-units
1476 +
1477 + scalableUnits: {
1478 'kilobits/s': {
1477 - 'bits/s' : 1 / 1000,
1479 + 'bits/s': 1 / 1000,
1480 'kilobits/s': 1,
1481 'megabits/s': 1000,
1482 'gigabits/s': 1000000
1483 },
1484 'kilobytes/s': {
1483 - 'bytes/s' : 1 / 1024,
1485 + 'bytes/s': 1 / 1024,
1486 'kilobytes/s': 1,
1487 'megabytes/s': 1024,
1488 'gigabytes/s': 1024 * 1024
1489 },
1490 'KB': {
1489 - 'B' : 1 / 1024,
1491 + 'B': 1 / 1024,
1492 'KB': 1,
1493 'MB': 1024,
1494 'GB': 1024 * 1024
@@ -1504,38 +1506,76 @@ var NETDATA = window.NETDATA || {};
1506 },
1507 'seconds': {
1508 'milliseconds': 0.001,
1507 - 'seconds' : 1,
1508 - 'minutes' : 60,
1509 - 'hours' : 3600,
1510 - 'days' : 86400
1509 + 'seconds': 1,
1510 + 'minutes': 60,
1511 + 'hours': 3600,
1512 + 'days': 86400
1513 + }
1514 + },
1515 +
1516 + convertibleUnits: {
1517 + 'Celsius': {
1518 + 'Fahrenheit': {
1519 + check: function() { return NETDATA.options.current.temperature === 'fahrenheit'; },
1520 + convert: function(value) { return value * 9 / 5 + 32; }
1521 + }
1522 + },
1523 + 'celsius': {
1524 + 'fahrenheit': {
1525 + check: function() { return NETDATA.options.current.temperature === 'fahrenheit'; },
1526 + convert: function(value) { return value * 9 / 5 + 32; }
1527 + }
1528 }
1529 },
1530
1531 + // get a function that converts the units
1532 + // + every time units are switched call the callback
1533 get: function(uuid, min, max, units, desired_units, common_units_name, switch_units_callback) {
1534 + // validate the parameters
1535 if(typeof units === 'undefined')
1536 units = 'undefined';
1537
1518 - if(typeof desired_units === 'undefined' || desired_units === null)
1519 - desired_units = 'original';
1538 + // check if we support units conversion
1539 + if(typeof NETDATA.unitsConversion.scalableUnits[units] === 'undefined' && typeof NETDATA.unitsConversion.convertibleUnits[units] === 'undefined') {
1540 + // we can't convert these units
1541 + //console.log('DEBUG: ' + uuid.toString() + ' can\'t convert units: ' + units.toString());
1542 + return function(value) { return value; };
1543 + }
1544
1521 - if(desired_units !== 'original' && typeof NETDATA.unitsConversion.units[units] !== 'undefined') {
1522 - // console.log('NETDATA.unitsConversion(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString());
1545 + // check if the caller wants the original units
1546 + if(typeof desired_units === 'undefined' || desired_units === null || desired_units === 'original' || desired_units === units) {
1547 + //console.log('DEBUG: ' + uuid.toString() + ' original units wanted');
1548 + switch_units_callback(units);
1549 + return function(value) { return value; };
1550 + }
1551
1524 - min = Math.abs(min);
1525 - max = Math.abs(max);
1526 - if (min > max) max = min;
1552 + // now we know we can convert the units
1553 + // and the caller wants some kind of conversion
1554
1528 - var divider = 1;
1529 - var x;
1555 + var tunits = null;
1556 + var tdivider = 0;
1557 + var x;
1558 +
1559 + if(typeof NETDATA.unitsConversion.scalableUnits[units] !== 'undefined') {
1560 + // units that can be scaled
1561 + // we decide a divider
1562 +
1563 + // console.log('NETDATA.unitsConversion(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString());
1564
1565 if (desired_units === 'auto') {
1532 - var tunits = null;
1533 - var tdivider = 0;
1566 + // the caller wants to auto-scale the units
1567 +
1568 + // find the absolute maximum value that is rendered on the chart
1569 + // based on this we decide the scale
1570 + min = Math.abs(min);
1571 + max = Math.abs(max);
1572 + if (min > max) max = min;
1573
1535 - for (x in NETDATA.unitsConversion.units[units]) {
1536 - if (!NETDATA.unitsConversion.units[units].hasOwnProperty(x)) continue;
1574 + // find the smallest scale that provides integers
1575 + for (x in NETDATA.unitsConversion.scalableUnits[units]) {
1576 + if (!NETDATA.unitsConversion.scalableUnits[units].hasOwnProperty(x)) continue;
1577
1538 - var m = NETDATA.unitsConversion.units[units][x];
1578 + var m = NETDATA.unitsConversion.scalableUnits[units][x];
1579
1580 if (m <= max && m > tdivider) {
1581 tunits = x;
@@ -1543,107 +1583,125 @@ var NETDATA = window.NETDATA || {};
1583 }
1584 }
1585
1546 - if(tunits !== null && typeof common_units_name === 'string' && typeof uuid === 'string') {
1547 - // console.log(uuid + ' name: ' + common_units_name + ', divider: ' + tdivider.toString() + ', units: ' + tunits);
1586 + if(tunits === null || tdivider <= 0) {
1587 + // we couldn't find one
1588 + //console.log('DEBUG: ' + uuid.toString() + ' cannot find an auto-scaling candidate for units: ' + units.toString() + ' (max: ' + max.toString() + ')');
1589 + switch_units_callback(units);
1590 + return function(value) { return value; };
1591 + }
1592
1593 + if(typeof common_units_name === 'string' && typeof uuid === 'string') {
1594 + // the caller wants several charts to have the same units
1595 + // data-common-units
1596 +
1597 + // add our divider into the list of keys
1598 var t = this.keys[common_units_name];
1599 if(typeof t === 'undefined') {
1551 - // add our commonUnits
1600 this.keys[common_units_name] = {};
1601 t = this.keys[common_units_name];
1602 }
1603 + t[uuid] = {
1604 + units: tunits,
1605 + divider: tdivider
1606 + };
1607
1556 - // add our divider
1557 - t[uuid] = tdivider;
1558 - var td = tdivider;
1559 -
1560 - // find the max divider
1561 - for(x in t)
1562 - if(t.hasOwnProperty(x) && t[x] > tdivider)
1563 - tdivider = t[x];
1564 -
1565 - // console.log(uuid + ' old divider ' + t[uuid].toString() + ', new divider ' + tdivider.toString());
1566 -
1567 - if(td !== tdivider) {
1568 - // find the name for this divider
1569 - tunits = null;
1570 - for (x in NETDATA.unitsConversion.units[units]) {
1571 - if (NETDATA.unitsConversion.units[units].hasOwnProperty(x) && NETDATA.unitsConversion.units[units][x] === tdivider) {
1572 - tunits = x;
1573 - break;
1574 - }
1575 - }
1608 + // find the max divider of all charts
1609 + var common_units = t[uuid];
1610 + for(x in t) {
1611 + if (t.hasOwnProperty(x) && t[x].divider > common_units.divider)
1612 + common_units = t[x];
1613 + }
1614
1577 - // console.log(uuid + ' new units ' + tunits);
1615 + // save our common_max to the latest keys
1616 + var latest = NETDATA.unitsConversion.latest[common_units_name];
1617 + if(typeof latest === 'undefined') {
1618 + NETDATA.unitsConversion.latest[common_units_name] = {};
1619 + latest = NETDATA.unitsConversion.latest[common_units_name];
1620 }
1621 + latest.units = common_units.units;
1622 + latest.divider = common_units.divider;
1623
1580 - if (tunits !== null && tdivider > 0) {
1581 - switch_units_callback(tunits);
1582 - divider = tdivider;
1583 -
1584 - var latest = NETDATA.unitsConversion.latest[common_units_name];
1585 - if(typeof latest === 'undefined') {
1586 - NETDATA.unitsConversion.latest[common_units_name] = {
1587 - units: tunits,
1588 - divider: tdivider
1589 - };
1590 - latest = NETDATA.unitsConversion.latest[common_units_name];
1591 - }
1592 - else {
1593 - latest.units = tunits;
1594 - latest.divider = tdivider;
1595 - }
1624 + tunits = latest.units;
1625 + tdivider = latest.divider;
1626
1597 - return function(value) {
1598 - if(tdivider !== latest.divider) {
1599 - // console.log(uuid + ' new units detected, old divider ' + tdivider.toString() + ', new divider ' + latest.divider.toString() + ' new units ' + latest.units);
1600 - switch_units_callback(latest.units);
1601 - tdivider = latest.divider;
1602 - }
1627 + //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', common-units=' + common_units_name.toString() + ((t[uuid].divider !== tdivider)?' USED COMMON, mine was ' + t[uuid].units:' set common').toString());
1628
1604 - return value / tdivider;
1629 + // apply it to this chart
1630 + switch_units_callback(tunits);
1631 + return function(value) {
1632 + if(tdivider !== latest.divider) {
1633 + // another chart switched our common units
1634 + // we should switch them too
1635 + //console.log('DEBUG: ' + uuid + ' switching units due to a common-units change, from ' + tunits.toString() + ' to ' + latest.units.toString());
1636 + tunits = latest.units;
1637 + tdivider = latest.divider;
1638 + switch_units_callback(tunits);
1639 }
1640 +
1641 + return value / tdivider;
1642 }
1643 }
1644 + else {
1645 + // the caller did not give data-common-units
1646 + // this chart auto-scales independently of all others
1647 + //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', autonomously');
1648
1609 - if (tunits !== null && tdivider > 0) {
1649 switch_units_callback(tunits);
1611 - divider = tdivider;
1650 + return function (value) { return value / tdivider; };
1651 }
1652 }
1653 else {
1615 - var found = 0;
1616 - for (x in NETDATA.unitsConversion.units[units]) {
1617 - if (!NETDATA.unitsConversion.units[units].hasOwnProperty(x)) continue;
1618 -
1619 - if (x === desired_units) {
1620 - found = 1;
1621 - switch_units_callback(x);
1622 - divider = NETDATA.unitsConversion.units[units][x];
1623 - break;
1624 - }
1654 + // the caller wants specific units
1655 +
1656 + if(typeof NETDATA.unitsConversion.scalableUnits[units][desired_units] !== 'undefined') {
1657 + // all good, set the new units
1658 + tdivider = NETDATA.unitsConversion.scalableUnits[units][desired_units];
1659 + // console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + desired_units.toString() + ' with divider ' + tdivider.toString() + ', by reference');
1660 + switch_units_callback(desired_units);
1661 + return function (value) { return value / tdivider; };
1662 }
1626 -
1627 - if(!found)
1663 + else {
1664 + // oops! switch back to original units
1665 console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.');
1666 + switch_units_callback(units);
1667 + return function (value) { return value; };
1668 + }
1669 }
1670 + }
1671 + else if(typeof NETDATA.unitsConversion.convertibleUnits[units] !== 'undefined') {
1672 + // units that can be converted
1673 + if(desired_units === 'auto') {
1674 + for(x in NETDATA.unitsConversion.convertibleUnits[units]) {
1675 + if (NETDATA.unitsConversion.convertibleUnits[units].hasOwnProperty(x)) {
1676 + if (NETDATA.unitsConversion.convertibleUnits[units][x].check()) {
1677 + //console.log('DEBUG: ' + uuid.toString() + ' converting ' + units.toString() + ' to: ' + x.toString());
1678 + switch_units_callback(x);
1679 + return NETDATA.unitsConversion.convertibleUnits[units][x].convert;
1680 + }
1681 + }
1682 + }
1683
1631 - if(divider <= 0 || divider === 1)
1632 - return function (value) {
1633 - return value;
1634 - };
1635 -
1636 - return function (value) {
1637 - // console.log('NETDATA.unitsConversion(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString() + ', divider = ' + divider.toString() + ', value = ' + value.toString());
1638 - return value / divider;
1639 - };
1640 - }
1641 - else {
1642 - return function (value) {
1643 - // console.log('NETDATA.unitsConversion(' + units.toString() + ', ' + desired_units.toString() + ', function()) dummy divider');
1644 - return value;
1645 - };
1646 - }
1684 + // none checked ok
1685 + //console.log('DEBUG: ' + uuid.toString() + ' no conversion available for ' + units.toString() + ' to: ' + desired_units.toString());
1686 + switch_units_callback(units);
1687 + return function (value) { return value; };
1688 + }
1689 + else if(typeof NETDATA.unitsConversion.convertibleUnits[units][desired_units] !== 'undefined') {
1690 + switch_units_callback(desired_units);
1691 + return NETDATA.unitsConversion.convertibleUnits[units][desired_units].convert;
1692 + }
1693 + else {
1694 + console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.');
1695 + switch_units_callback(units);
1696 + return function (value) { return value; };
1697 + }
1698 + }
1699 + else {
1700 + // hm... did we forget to implement the new type?
1701 + console.log('Unmatched unit conversion method for units ' + units.toString());
1702 + switch_units_callback(units);
1703 + return function (value) { return value; };
1704 + }
1705 }
1706 };
1707
web/index.html
+39 -10
@@ -2731,6 +2731,13 @@
2731 self.bootstrapToggle(NETDATA.getOption('units') === 'auto' ? 'on' : 'off');
2732 }
2733 };
2734 + var temp_sync_option = function(option) {
2735 + var self = $('#' + option);
2736 +
2737 + if(self.prop('checked') !== (NETDATA.getOption('temperature') === 'celsius')) {
2738 + self.bootstrapToggle(NETDATA.getOption('temperature') === 'celsius' ? 'on' : 'off');
2739 + }
2740 + };
2741
2742 sync_option('eliminate_zero_dimensions');
2743 sync_option('destroy_on_hide');
@@ -2745,6 +2752,7 @@
2752 sync_option('show_help');
2753 theme_sync_option('netdata_theme_control');
2754 units_sync_option('units_conversion');
2755 + temp_sync_option('units_temp');
2756
2757 if(NETDATA.getOption('parallel_refresher') === false) {
2758 $('#concurrent_refreshes_row').hide();
@@ -2774,7 +2782,9 @@
2782
2783 $('#units_conversion').change(function() {
2784 NETDATA.setOption('units', $(this).prop('checked')?'auto':'original');
2777 - netdataReload();
2785 + });
2786 + $('#units_temp').change(function() {
2787 + NETDATA.setOption('temperature', $(this).prop('checked')?'celsius':'fahrenheit');
2788 });
2789
2790 $('#show_help').change(function() {
@@ -3309,6 +3319,7 @@
3319 <li role="presentation" class="active"><a href="#settings_performance" aria-controls="settings_performance" role="tab" data-toggle="tab">Performance</a></li>
3320 <li role="presentation"><a href="#settings_sync" aria-controls="settings_sync" role="tab" data-toggle="tab">Synchronization</a></li>
3321 <li role="presentation"><a href="#settings_visual" aria-controls="settings_visual" role="tab" data-toggle="tab">Visual</a></li>
3322 + <li role="presentation"><a href="#settings_locale" aria-controls="settings_locale" role="tab" data-toggle="tab">Locale</a></li>
3323 </ul>
3324
3325 <!-- Tab panes -->
@@ -3401,15 +3412,6 @@
3412 </small>
3413 </td>
3414 </tr>
3404 - <tr class="option-row">
3405 - <td class="option-control"><input id="units_conversion" type="checkbox" checked data-toggle="toggle" data-on="Scale Units" data-off="Original" data-width="110px"></td>
3406 - <td class="option-info"><strong>Enable auto-conversion of units?</strong><br/>
3407 - <small>When set to <b>Scale Units</b> the values shown will be scaled to the maximum unit (ie. kilobits will be scaled to megabits or even gigabits). When set to <b>Original</b> all the values will be rendered using the original units maintained by the server.
3408 - <br/>
3409 - <b>Switching this will reload the dashboard</b>.
3410 - </small>
3411 - </td>
3412 - </tr>
3415 <tr class="option-row">
3416 <td class="option-control"><input id="pan_and_zoom_data_padding" type="checkbox" checked data-toggle="toggle" data-on="Pad" data-off="Don't Pad" data-width="110px"></td>
3417 <td class="option-info"><strong>Enable data padding when panning and zooming?</strong><br/>
@@ -3428,6 +3430,33 @@
3430 </div>
3431 </form>
3432 </div>
3433 + <div role="tabpanel" class="tab-pane" id="settings_locale">
3434 + <form id="optionsForm4" method="get" class="form-horizontal">
3435 + <div class="form-group">
3436 + <table>
3437 + <tr class="option-row">
3438 + <td class="option-control"><input id="units_conversion" type="checkbox" checked data-toggle="toggle" data-on="Scale Units" data-off="Fixed Units" data-width="110px"></td>
3439 + <td class="option-info"><strong>Enable auto-scaling of select units?</strong><br/>
3440 + <small>When set to <b>Scale Units</b> the values shown will dynamically be scaled (e.g. 1000 kilobits will be shown as 1 megabit).
3441 + When set to <b>Fixed Units</b> all the values will be rendered using the original units maintained by the netdata server.
3442 + <br/>
3443 + <b>This setting will be applied gradually as charts are updated. To force it, refresh the dashboard</b>.
3444 + </small>
3445 + </td>
3446 + </tr>
3447 + <tr class="option-row">
3448 + <td class="option-control"><input id="units_temp" type="checkbox" checked data-toggle="toggle" data-on="Celsius" data-off="Fahrenheit" data-width="110px"></td>
3449 + <td class="option-info"><strong>Which units to use for temperatures?</strong><br/>
3450 + <small>Set the temperature units of the dashboard.
3451 + <br/>
3452 + <b>This setting will be applied gradually as charts are updated. To force it, refresh the dashboard</b>.
3453 + </small>
3454 + </td>
3455 + </tr>
3456 + </table>
3457 + </div>
3458 + </form>
3459 + </div>
3460 </div>
3461 </div>
3462 <div class="modal-footer">