summaryrefslogtreecommitdiffstats
path: root/web/gui/src/dashboard.js/units-conversion.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/gui/src/dashboard.js/units-conversion.js')
-rw-r--r--web/gui/src/dashboard.js/units-conversion.js528
1 files changed, 528 insertions, 0 deletions
diff --git a/web/gui/src/dashboard.js/units-conversion.js b/web/gui/src/dashboard.js/units-conversion.js
new file mode 100644
index 00000000..b0dbd293
--- /dev/null
+++ b/web/gui/src/dashboard.js/units-conversion.js
@@ -0,0 +1,528 @@
+NETDATA.unitsConversion = {
+ keys: {}, // keys for data-common-units
+ latest: {}, // latest selected units for data-common-units
+
+ globalReset: function () {
+ this.keys = {};
+ this.latest = {};
+ },
+
+ scalableUnits: {
+ 'packets/s': {
+ 'pps': 1,
+ 'Kpps': 1000,
+ 'Mpps': 1000000
+ },
+ 'pps': {
+ 'pps': 1,
+ 'Kpps': 1000,
+ 'Mpps': 1000000
+ },
+ 'kilobits/s': {
+ 'bits/s': 1 / 1000,
+ 'kilobits/s': 1,
+ 'megabits/s': 1000,
+ 'gigabits/s': 1000000,
+ 'terabits/s': 1000000000
+ },
+ 'bytes/s': {
+ 'bytes/s': 1,
+ 'kilobytes/s': 1024,
+ 'megabytes/s': 1024 * 1024,
+ 'gigabytes/s': 1024 * 1024 * 1024,
+ 'terabytes/s': 1024 * 1024 * 1024 * 1024
+ },
+ 'kilobytes/s': {
+ 'bytes/s': 1 / 1024,
+ 'kilobytes/s': 1,
+ 'megabytes/s': 1024,
+ 'gigabytes/s': 1024 * 1024,
+ 'terabytes/s': 1024 * 1024 * 1024
+ },
+ 'B/s': {
+ 'B/s': 1,
+ 'KiB/s': 1024,
+ 'MiB/s': 1024 * 1024,
+ 'GiB/s': 1024 * 1024 * 1024,
+ 'TiB/s': 1024 * 1024 * 1024 * 1024
+ },
+ 'KB/s': {
+ 'B/s': 1 / 1024,
+ 'KB/s': 1,
+ 'MB/s': 1024,
+ 'GB/s': 1024 * 1024,
+ 'TB/s': 1024 * 1024 * 1024
+ },
+ 'KiB/s': {
+ 'B/s': 1 / 1024,
+ 'KiB/s': 1,
+ 'MiB/s': 1024,
+ 'GiB/s': 1024 * 1024,
+ 'TiB/s': 1024 * 1024 * 1024
+ },
+ 'B': {
+ 'B': 1,
+ 'KiB': 1024,
+ 'MiB': 1024 * 1024,
+ 'GiB': 1024 * 1024 * 1024,
+ 'TiB': 1024 * 1024 * 1024 * 1024,
+ 'PiB': 1024 * 1024 * 1024 * 1024 * 1024
+ },
+ 'KB': {
+ 'B': 1 / 1024,
+ 'KB': 1,
+ 'MB': 1024,
+ 'GB': 1024 * 1024,
+ 'TB': 1024 * 1024 * 1024
+ },
+ 'KiB': {
+ 'B': 1 / 1024,
+ 'KiB': 1,
+ 'MiB': 1024,
+ 'GiB': 1024 * 1024,
+ 'TiB': 1024 * 1024 * 1024
+ },
+ 'MB': {
+ 'B': 1 / (1024 * 1024),
+ 'KB': 1 / 1024,
+ 'MB': 1,
+ 'GB': 1024,
+ 'TB': 1024 * 1024,
+ 'PB': 1024 * 1024 * 1024
+ },
+ 'MiB': {
+ 'B': 1 / (1024 * 1024),
+ 'KiB': 1 / 1024,
+ 'MiB': 1,
+ 'GiB': 1024,
+ 'TiB': 1024 * 1024,
+ 'PiB': 1024 * 1024 * 1024
+ },
+ 'GB': {
+ 'B': 1 / (1024 * 1024 * 1024),
+ 'KB': 1 / (1024 * 1024),
+ 'MB': 1 / 1024,
+ 'GB': 1,
+ 'TB': 1024,
+ 'PB': 1024 * 1024,
+ 'EB': 1024 * 1024 * 1024
+ },
+ 'GiB': {
+ 'B': 1 / (1024 * 1024 * 1024),
+ 'KiB': 1 / (1024 * 1024),
+ 'MiB': 1 / 1024,
+ 'GiB': 1,
+ 'TiB': 1024,
+ 'PiB': 1024 * 1024,
+ 'EiB': 1024 * 1024 * 1024
+ },
+ 'num': {
+ 'num': 1,
+ 'num (K)': 1000,
+ 'num (M)': 1000000,
+ 'num (G)': 1000000000,
+ 'num (T)': 1000000000000
+ }
+ /*
+ 'milliseconds': {
+ 'seconds': 1000
+ },
+ 'seconds': {
+ 'milliseconds': 0.001,
+ 'seconds': 1,
+ 'minutes': 60,
+ 'hours': 3600,
+ 'days': 86400
+ }
+ */
+ },
+
+ convertibleUnits: {
+ 'Celsius': {
+ 'Fahrenheit': {
+ check: function (max) {
+ void(max);
+ return NETDATA.options.current.temperature === 'fahrenheit';
+ },
+ convert: function (value) {
+ return value * 9 / 5 + 32;
+ }
+ }
+ },
+ 'celsius': {
+ 'fahrenheit': {
+ check: function (max) {
+ void(max);
+ return NETDATA.options.current.temperature === 'fahrenheit';
+ },
+ convert: function (value) {
+ return value * 9 / 5 + 32;
+ }
+ }
+ },
+ 'seconds': {
+ 'time': {
+ check: function (max) {
+ void(max);
+ return NETDATA.options.current.seconds_as_time;
+ },
+ convert: function (seconds) {
+ return NETDATA.unitsConversion.seconds2time(seconds);
+ }
+ }
+ },
+ 'milliseconds': {
+ 'milliseconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time && max < 1000;
+ },
+ convert: function (milliseconds) {
+ let tms = Math.round(milliseconds * 10);
+ milliseconds = Math.floor(tms / 10);
+
+ tms -= milliseconds * 10;
+
+ return (milliseconds).toString() + '.' + tms.toString();
+ }
+ },
+ 'seconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time && max >= 1000 && max < 60000;
+ },
+ convert: function (milliseconds) {
+ milliseconds = Math.round(milliseconds);
+
+ let seconds = Math.floor(milliseconds / 1000);
+ milliseconds -= seconds * 1000;
+
+ milliseconds = Math.round(milliseconds / 10);
+
+ return seconds.toString() + '.'
+ + NETDATA.zeropad(milliseconds);
+ }
+ },
+ 'M:SS.ms': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time && max >= 60000;
+ },
+ convert: function (milliseconds) {
+ milliseconds = Math.round(milliseconds);
+
+ let minutes = Math.floor(milliseconds / 60000);
+ milliseconds -= minutes * 60000;
+
+ let seconds = Math.floor(milliseconds / 1000);
+ milliseconds -= seconds * 1000;
+
+ milliseconds = Math.round(milliseconds / 10);
+
+ return minutes.toString() + ':'
+ + NETDATA.zeropad(seconds) + '.'
+ + NETDATA.zeropad(milliseconds);
+ }
+ }
+ },
+ 'nanoseconds': {
+ 'nanoseconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time && max < 1000;
+ },
+ convert: function (nanoseconds) {
+ let tms = Math.round(nanoseconds * 10);
+ nanoseconds = Math.floor(tms / 10);
+
+ tms -= nanoseconds * 10;
+
+ return (nanoseconds).toString() + '.' + tms.toString();
+ }
+ },
+ 'microseconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time
+ && max >= 1000 && max < 1000 * 1000;
+ },
+ convert: function (nanoseconds) {
+ nanoseconds = Math.round(nanoseconds);
+
+ let microseconds = Math.floor(nanoseconds / 1000);
+ nanoseconds -= microseconds * 1000;
+
+ nanoseconds = Math.round(nanoseconds / 10 );
+
+ return microseconds.toString() + '.'
+ + NETDATA.zeropad(nanoseconds);
+ }
+ },
+ 'milliseconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time
+ && max >= 1000 * 1000 && max < 1000 * 1000 * 1000;
+ },
+ convert: function (nanoseconds) {
+ nanoseconds = Math.round(nanoseconds);
+
+ let milliseconds = Math.floor(nanoseconds / 1000 / 1000);
+ nanoseconds -= milliseconds * 1000 * 1000;
+
+ nanoseconds = Math.round(nanoseconds / 1000 / 10);
+
+ return milliseconds.toString() + '.'
+ + NETDATA.zeropad(nanoseconds);
+ }
+ },
+ 'seconds': {
+ check: function (max) {
+ return NETDATA.options.current.seconds_as_time
+ && max >= 1000 * 1000 * 1000;
+ },
+ convert: function (nanoseconds) {
+ nanoseconds = Math.round(nanoseconds);
+
+ let seconds = Math.floor(nanoseconds / 1000 / 1000 / 1000);
+ nanoseconds -= seconds * 1000 * 1000 * 1000;
+
+ nanoseconds = Math.round(nanoseconds / 1000 / 1000 / 10);
+
+ return seconds.toString() + '.'
+ + NETDATA.zeropad(nanoseconds);
+ }
+ },
+ }
+ },
+
+ seconds2time: function (seconds) {
+ seconds = Math.abs(seconds);
+
+ let days = Math.floor(seconds / 86400);
+ seconds -= days * 86400;
+
+ let hours = Math.floor(seconds / 3600);
+ seconds -= hours * 3600;
+
+ let minutes = Math.floor(seconds / 60);
+ seconds -= minutes * 60;
+
+ seconds = Math.round(seconds);
+
+ let ms_txt = '';
+ /*
+ let ms = seconds - Math.floor(seconds);
+ seconds -= ms;
+ ms = Math.round(ms * 1000);
+
+ if (ms > 1) {
+ if (ms < 10)
+ ms_txt = '.00' + ms.toString();
+ else if (ms < 100)
+ ms_txt = '.0' + ms.toString();
+ else
+ ms_txt = '.' + ms.toString();
+ }
+ */
+
+ return ((days > 0) ? days.toString() + 'd:' : '').toString()
+ + NETDATA.zeropad(hours) + ':'
+ + NETDATA.zeropad(minutes) + ':'
+ + NETDATA.zeropad(seconds)
+ + ms_txt;
+ },
+
+ // get a function that converts the units
+ // + every time units are switched call the callback
+ get: function (uuid, min, max, units, desired_units, common_units_name, switch_units_callback) {
+ // validate the parameters
+ if (typeof units === 'undefined') {
+ units = 'undefined';
+ }
+
+ // check if we support units conversion
+ if (typeof this.scalableUnits[units] === 'undefined' && typeof this.convertibleUnits[units] === 'undefined') {
+ // we can't convert these units
+ //console.log('DEBUG: ' + uuid.toString() + ' can\'t convert units: ' + units.toString());
+ return function (value) {
+ return value;
+ };
+ }
+
+ // check if the caller wants the original units
+ if (typeof desired_units === 'undefined' || desired_units === null || desired_units === 'original' || desired_units === units) {
+ //console.log('DEBUG: ' + uuid.toString() + ' original units wanted');
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ }
+
+ // now we know we can convert the units
+ // and the caller wants some kind of conversion
+
+ let tunits = null;
+ let tdivider = 0;
+
+ if (typeof this.scalableUnits[units] !== 'undefined') {
+ // units that can be scaled
+ // we decide a divider
+
+ // console.log('NETDATA.unitsConversion.get(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString());
+
+ if (desired_units === 'auto') {
+ // the caller wants to auto-scale the units
+
+ // find the absolute maximum value that is rendered on the chart
+ // based on this we decide the scale
+ min = Math.abs(min);
+ max = Math.abs(max);
+ if (min > max) {
+ max = min;
+ }
+
+ // find the smallest scale that provides integers
+ // for (x in this.scalableUnits[units]) {
+ // if (this.scalableUnits[units].hasOwnProperty(x)) {
+ // let m = this.scalableUnits[units][x];
+ // if (m <= max && m > tdivider) {
+ // tunits = x;
+ // tdivider = m;
+ // }
+ // }
+ // }
+ const sunit = this.scalableUnits[units];
+ for (var x of Object.keys(sunit)) {
+ let m = sunit[x];
+ if (m <= max && m > tdivider) {
+ tunits = x;
+ tdivider = m;
+ }
+ }
+
+ if (tunits === null || tdivider <= 0) {
+ // we couldn't find one
+ //console.log('DEBUG: ' + uuid.toString() + ' cannot find an auto-scaling candidate for units: ' + units.toString() + ' (max: ' + max.toString() + ')');
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ }
+
+ if (typeof common_units_name === 'string' && typeof uuid === 'string') {
+ // the caller wants several charts to have the same units
+ // data-common-units
+
+ let common_units_key = common_units_name + '-' + units;
+
+ // add our divider into the list of keys
+ let t = this.keys[common_units_key];
+ if (typeof t === 'undefined') {
+ this.keys[common_units_key] = {};
+ t = this.keys[common_units_key];
+ }
+ t[uuid] = {
+ units: tunits,
+ divider: tdivider
+ };
+
+ // find the max divider of all charts
+ let common_units = t[uuid];
+ for (var x in t) {
+ if (t.hasOwnProperty(x) && t[x].divider > common_units.divider) {
+ common_units = t[x];
+ }
+ }
+
+ // save our common_max to the latest keys
+ let latest = this.latest[common_units_key];
+ if (typeof latest === 'undefined') {
+ this.latest[common_units_key] = {};
+ latest = this.latest[common_units_key];
+ }
+ latest.units = common_units.units;
+ latest.divider = common_units.divider;
+
+ tunits = latest.units;
+ tdivider = latest.divider;
+
+ //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());
+
+ // apply it to this chart
+ switch_units_callback(tunits);
+ return function (value) {
+ if (tdivider !== latest.divider) {
+ // another chart switched our common units
+ // we should switch them too
+ //console.log('DEBUG: ' + uuid + ' switching units due to a common-units change, from ' + tunits.toString() + ' to ' + latest.units.toString());
+ tunits = latest.units;
+ tdivider = latest.divider;
+ switch_units_callback(tunits);
+ }
+
+ return value / tdivider;
+ };
+ } else {
+ // the caller did not give data-common-units
+ // this chart auto-scales independently of all others
+ //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', autonomously');
+
+ switch_units_callback(tunits);
+ return function (value) {
+ return value / tdivider;
+ };
+ }
+ } else {
+ // the caller wants specific units
+
+ if (typeof this.scalableUnits[units][desired_units] !== 'undefined') {
+ // all good, set the new units
+ tdivider = this.scalableUnits[units][desired_units];
+ // console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + desired_units.toString() + ' with divider ' + tdivider.toString() + ', by reference');
+ switch_units_callback(desired_units);
+ return function (value) {
+ return value / tdivider;
+ };
+ } else {
+ // oops! switch back to original units
+ console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.');
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ }
+ }
+ } else if (typeof this.convertibleUnits[units] !== 'undefined') {
+ // units that can be converted
+ if (desired_units === 'auto') {
+ for (var x in this.convertibleUnits[units]) {
+ if (this.convertibleUnits[units].hasOwnProperty(x)) {
+ if (this.convertibleUnits[units][x].check(max)) {
+ //console.log('DEBUG: ' + uuid.toString() + ' converting ' + units.toString() + ' to: ' + x.toString());
+ switch_units_callback(x);
+ return this.convertibleUnits[units][x].convert;
+ }
+ }
+ }
+
+ // none checked ok
+ //console.log('DEBUG: ' + uuid.toString() + ' no conversion available for ' + units.toString() + ' to: ' + desired_units.toString());
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ } else if (typeof this.convertibleUnits[units][desired_units] !== 'undefined') {
+ switch_units_callback(desired_units);
+ return this.convertibleUnits[units][desired_units].convert;
+ } else {
+ console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.');
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ }
+ } else {
+ // hm... did we forget to implement the new type?
+ console.log(`Unmatched unit conversion method for units ${units.toString()}`);
+ switch_units_callback(units);
+ return function (value) {
+ return value;
+ };
+ }
+ }
+};