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">