123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- module.exports = function(RED) {
- var ui = require('../ui')(RED);
- var ChartIdList = {};
-
- function ChartNode(config) {
- RED.nodes.createNode(this, config);
- this.chartType = config.chartType || "line";
- var node = this;
- var group = RED.nodes.getNode(config.group);
- if (!group) { return; }
- var tab = RED.nodes.getNode(group.config.tab);
- if (!tab) { return; }
- if (config.width === "0") { delete config.width; }
- if (config.height === "0") { delete config.height; }
- // number of pixels wide the chart will be... 43 = sizes.sx - sizes.px
- //var pixelsWide = ((config.width || group.config.width || 6) - 1) * 43 - 15;
- if (!tab || !group) { return; }
- var dnow = Date.now();
- var options = {
- emitOnlyNewValues: true,
- node: node,
- tab: tab,
- group: group,
- control: {
- type: 'chart',
- look: node.chartType,
- order: config.order,
- label: config.label,
- legend: config.legend || false,
- interpolate: config.interpolate,
- nodata: config.nodata,
- width: parseInt(config.width || group.config.width || 6),
- height: parseInt(config.height || group.config.width/2+1 || 4),
- ymin: config.ymin,
- ymax: config.ymax,
- dot: config.dot || false,
- xformat : config.xformat || "HH:mm:ss",
- cutout: parseInt(config.cutout || 0),
- colors: config.colors,
- useOneColor: config.useOneColor || false,
- useUTC: config.useUTC || false,
- animation: false,
- spanGaps: false,
- useDifferentColor: config.useDifferentColor || false,
- options: {},
- className: config.className || '',
- },
- convertBack: function(data) {
- if (data) {
- if (data[0] && data[0].hasOwnProperty("values")) {
- return [data[0].values];
- }
- if (data.length == 0) {
- return [];
- }
- }
- },
- convert: function(value, oldValue, msg) {
- var converted = {};
- if (ChartIdList.hasOwnProperty(node.id) && ChartIdList[node.id] !== node.chartType) {
- value = [];
- }
- if (this.control.look !== node.chartType) {
- if ((this.control.look === "line") || (node.chartType === "line")) { value = []; }
- node.chartType = this.control.look;
- }
- ChartIdList[node.id] = node.chartType;
- if (Array.isArray(value)) {
- if (value.length === 0) { // reset chart
- converted.update = false;
- converted.updatedValues = [];
- return converted;
- }
- if (value[0].hasOwnProperty("series") && value[0].hasOwnProperty("data")) {
- if (!Array.isArray(value[0].series)) { node.error("series not array",msg); return; }
- if (!Array.isArray(value[0].data)) { node.error("Data not array",msg); return; }
- var flag = true;
- for (var dd = 0; dd < value[0].data.length; dd++ ) {
- if (!isNaN(value[0].data[dd][0])) { flag = false; }
- }
- if (node.chartType === "line") {
- if (flag) { delete value[0].labels; }
- if (config.removeOlderPoints) {
- for (var dl=0; dl < value[0].data.length; dl++ ) {
- if (value[0].data[dl].length > config.removeOlderPoints) {
- value[0].data[dl] = value[0].data[dl].slice(-config.removeOlderPoints);
- }
- }
- }
- }
- else if (node.chartType === "bar" || node.chartType === "horizontalBar") {
- if (flag) {
- var tmp = [];
- for (var d=0; d<value[0].data.length; d++) {
- tmp.push([value[0].data[d]]);
- }
- value[0].data = tmp;
- var tmp2 = value[0].series;
- value[0].series = value[0].labels;
- value[0].labels = tmp2;
- }
- }
- value = [{ key:node.id, values:(value[0] || {series:[], data:[], labels:[]}) }];
- }
- else {
- node.warn("Bad data inject");
- value = oldValue;
- }
- converted.update = false;
- converted.updatedValues = value;
- }
- else {
- if (value === false) { value = null; } // let false also create gaps in chart
- if (value !== null) { // let null object through for gaps
- value = parseFloat(value); // only handle numbers
- if (isNaN(value)) { return; } // return if not a number
- }
- converted.newPoint = true;
- var label = msg.label || " ";
- var series = msg.series || msg.topic || "";
- //if (node.chartType === "bar" || node.chartType === "horizontalBar" || node.chartType === "pie") {
- if (node.chartType !== "line") {
- if (!msg.series) {
- label = msg.topic || msg.label || " ";
- series = msg.series || "";
- }
- }
- if ((!oldValue) || (oldValue.length === 0)) {
- oldValue = [{ key:node.id, values:{ series:[], data:[], labels:[] } }];
- }
- //if (node.chartType === "line" || node.chartType === "pie" || node.chartType === "bar" || node.chartType === "horizontalBar" || node.chartType === "radar") { // Line, Bar and Radar
- var refill = false;
- if (node.chartType === "line") { label = ""; }
- var s = oldValue[0].values.series.indexOf(series);
- if (!oldValue[0].values.hasOwnProperty("labels")) { oldValue[0].values.labels = []; }
- var l = oldValue[0].values.labels.indexOf(label);
- if (s === -1) {
- oldValue[0].values.series.push(series);
- s = oldValue[0].values.series.length - 1;
- oldValue[0].values.data[s] = [];
- if (l > 0) { refill = true; }
- }
- if (l === -1) {
- oldValue[0].values.labels.push(label);
- l = oldValue[0].values.labels.length - 1;
- if (l > 0) { refill = true; }
- }
- if (node.chartType === "line") {
- var time;
- if (msg.timestamp !== undefined) { time = new Date(msg.timestamp).getTime(); }
- else { time = new Date().getTime(); }
- var limitOffsetSec = parseInt(config.removeOlder) * parseInt(config.removeOlderUnit);
- var limitTime = time - limitOffsetSec * 1000;
- if (time < limitTime) { return oldValue; } // ignore if too old for window
- var point = { "x":time, "y":value };
- oldValue[0].values.data[s].push(point);
- converted.newPoint = [{ key:node.id, update:true, values:{ series:series, data:point, labels:label } }];
- var rc = 0;
- for (var u = 0; u < oldValue[0].values.data[s].length; u++) {
- if (oldValue[0].values.data[s][u].x >= limitTime) { break; } // stop as soon as we are in time window.
- else { rc += 1; }
- }
- if (rc > 0) { oldValue[0].values.data[s].splice(0,rc); }
- if (config.removeOlderPoints) {
- var rc2 = oldValue[0].values.data[s].length-config.removeOlderPoints;
- if (rc2 > 0) { oldValue[0].values.data[s].splice(0,rc2); rc = rc2;}
- }
- if (rc > 0) { converted.newPoint[0].remove = rc; }
- var swap; // insert correctly if a timestamp was earlier.
- for (var t = oldValue[0].values.data[s].length-2; t>=0; t--) {
- if (oldValue[0].values.data[s][t].x <= time) {
- break; // stop if we are in the right place
- }
- else {
- swap = oldValue[0].values.data[s][t];
- oldValue[0].values.data[s][t] = oldValue[0].values.data[s][t+1];
- oldValue[0].values.data[s][t+1] = swap;
- }
- }
- if (swap) { converted.newPoint = true; } // if inserted then update whole chart
-
- if (Date.now() > (dnow + 60000)) {
- dnow = Date.now();
- for (var x = 0; x < oldValue[0].values.data.length; x++) {
- for (var y = 0; y < oldValue[0].values.data[x].length; y++) {
- if (oldValue[0].values.data[x][y].x >= limitTime) {
- break; // stop as soon as we are in time window.
- }
- else {
- oldValue[0].values.data[x].splice(0,1);
- converted.newPoint = true;
- y = y - 1;
- }
- }
- }
- }
-
- }
- else {
- oldValue[0].values.data[s][l] = value;
- if (refill) {
- for (var i = 0; i < oldValue[0].values.series.length; i++) {
- for (var k = 0; k < oldValue[0].values.labels.length; k++) {
- oldValue[0].values.data[i][k] = oldValue[0].values.data[i][k] || null;
- }
- }
- }
- }
- converted.update = true;
- converted.updatedValues = oldValue;
- }
- return converted;
- }
- };
-
- var chgtab = function() {
- node.receive({payload:"R"});
- };
- ui.ev.on('changetab', chgtab);
-
- var done = ui.add(options);
-
- var st = setTimeout(function() {
- node.emit("input",{payload:"start"}); // trigger a redraw at start to flush out old data.
- }, 100);
-
- node.on("close", function() {
- if (st) { clearTimeout(st); }
- ui.ev.removeListener('changetab', chgtab);
- done();
- })
- }
- RED.nodes.registerType("ui_chart", ChartNode);
- };
|