class PcolorBuffer{ constructor({div, data, key, props}){ this.div = document.getElementById(div); this.n = 0 this.divs = []; this.wait = false; this.key = key; this.timer = (props.throttle || 30)*1000; this.lastRan = Date.now(); this.lastFunc = null; this.zbuffer = []; this.xbuffer = []; this.empty = Array(data.yrange.length).fill(null); this.props = props; this.setup(data); } setup(data){ this.last = data.time.slice(-1); if (data.time.length == 1){ var values = { 'time': data.time, 'data': data[this.key].map(function(x) {return [x]})}; }else{ var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length); } var t = values.time.map(function(x) { return new Date(x*1000); }); for (var i = 0; i < data[this.key].length; i++){ var layout = { xaxis: { title: 'Time', showgrid: false, }, yaxis: { title: 'km', showgrid: false, }, titlefont: { size: 14, } }; var iDiv = document.createElement('div'); iDiv.id = 'plot-' + i; this.zbuffer.push([]); this.n = this.n + 1; this.div.appendChild(iDiv); this.divs.push(iDiv.id); var trace = { z: values.data[i], x: t, y: data.yrange, colorscale: this.props.colormap || 'Viridis', transpose: true, type: 'heatmap' }; if (this.props.zmin) {trace.zmin = this.props.zmin} if (this.props.zmax) {trace.zmax = this.props.zmax} layout.title = 'Channel ' + i + ' - ' + t.slice(-1).toLocaleString(); var conf = { modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'], displaylogo: false, showTips: true }; Plotly.newPlot('plot-'+i, [trace], layout, conf); } } getSize(){ var div = document.getElementById(this.divs[0]); var t = this.xbuffer.slice(-1)[0]; var n = 0; var timespan = (this.props.timespan || 8)*1000*60*60; while ((t-div.data[0].x[n]) > timespan){ n += 1; } return n; } fill_gaps(xBuffer, zBuffer, interval, N){ var x = [xBuffer[0]]; var z = []; var last; for (var j = 0; j < N; j++){ z.push([zBuffer[j][0]]); } for (var i = 1; i < xBuffer.length; i++){ var cnt = 0; last = x.slice(-1)[0]; while (Math.abs(parseFloat(xBuffer[i])-last-parseFloat(interval))>0.5){ cnt += 1; last = last+interval; x.push(last); for (var j = 0; j < N; j++){ z[j].push(this.empty); } // Avoid infinite loop if (cnt==100){break;} } x.push(xBuffer[i]); for (var j = 0; j < N; j++){ z[j].push(zBuffer[j][i]); } } return {'time':x, 'data':z}; } plot(){ // add new data to plots and empty buffers var N = this.getSize(); console.log('Plotting...'); for (var i = 0; i < this.n; i++){ var div = document.getElementById(this.divs[i]); if (N > 0){ div.data[0].z = div.data[0].z.slice(N,) div.data[0].x = div.data[0].x.slice(N,) } Plotly.extendTraces(div, { z: [this.zbuffer[i]], x: [this.xbuffer] }, [0]); this.zbuffer[i] = []; } this.xbuffer = []; } update(obj){ // fill data gaps var cnt = 0; while (Math.abs(parseFloat(obj.time[0])-this.last-parseFloat(obj.interval))>0.5){ cnt += 1; this.last += obj.interval; var newt = new Date((this.last)*1000); this.xbuffer.push(newt); for (var i = 0; i < obj[this.key].length; i++){ this.zbuffer[i].push(this.empty); } // Avoid infinite loop if (cnt==100){break;} } // update buffers this.last = parseFloat(obj.time[0]); var t = new Date(obj.time[0]*1000); this.xbuffer.push(t); for (var i = 0; i < obj[this.key].length; i++){ this.zbuffer[i].push(obj[this.key][i]); // update title var div = document.getElementById(this.divs[i]); Plotly.relayout(div, { title: 'Channel ' + i + ' - ' + t.toLocaleString(), }); } // plot when ready (every 10 secs) if (!this.wait){ this.plot(); this.wait = true; } else { clearTimeout(this.lastFunc) this.lastFunc = setTimeout(function(scope) { if ((Date.now() - scope.lastRan) >= scope.timer) { scope.plot() scope.lastRan = Date.now() } }, this.timer - (Date.now() - this.lastRan), this) } } } class Pcolor{ constructor({div, data, props}){ this.div = document.getElementById(div); this.n = 0 this.divs = []; this.props = props; this.setup(data); } setup(data){ console.log(data); for (var i = 0; i < data.spc.length; i++){ var layout = { height: 400, xaxis: { title: 'Velocity', showgrid: false, zeroline: false, domain: [0, 0.7], }, yaxis: { title: 'km', showgrid: false, }, xaxis2: { title: 'dB', domain: [0.75, 1], ticks: 'outside', }, titlefont: { size: 14, } }; var iDiv = document.createElement('div'); iDiv.id = 'plot-' + i; iDiv.className += iDiv.className ? ' col-md-6' : 'col-md-6'; this.n = this.n + 1; this.div.appendChild(iDiv); this.divs.push(iDiv.id); var trace1 = { z: data.spc[i], x: data.xrange, y: data.yrange, colorscale: this.props.colormap || 'Viridis', transpose: true, type: 'heatmap' }; var trace2 = { x: data.rti[i], y: data.yrange, xaxis: 'x2', yaxis: 'y', type: 'scatter', }; if (this.props.zmin) { trace1.zmin = this.props.zmin } if (this.props.zmax) { trace1.zmax = this.props.zmax; layout.xaxis2.range = [this.props.zmin, this.props.zmax] } var t = new Date(data.time*1000); layout.title = 'Channel ' + i + ': ' + data.noise[i]+'dB - ' + t.toLocaleString(); var conf = { modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'], displaylogo: false, showTips: true }; Plotly.newPlot('plot-'+i, [trace1, trace2], layout, conf); } } plot(obj){ // add new data to plots and empty buffers console.log('Plotting...'); var t = new Date(obj.time[0]*1000); for (var i = 0; i < this.n; i++){ var div = document.getElementById(this.divs[i]); Plotly.relayout(div, { title: 'Channel ' + i + ': ' + obj.noise[i]+'dB - ' + t.toLocaleString(), }); Plotly.restyle(div, { z: [obj.spc[i], null], x: [obj.xrange, obj.rti[i]] }, [0, 1]); } } } class Scatter{ constructor({div, data, key, props}){ this.div = document.getElementById(div); this.n = 0 this.key = key; this.wait = false; this.timer = (props.throttle || 30)*1000; this.lastRan = Date.now(); this.lastFunc = null; this.ybuffer = []; this.xbuffer = []; this.props = props; this.setup(data); } setup(data){ var traces = []; this.last = data.time.slice(-1); if (data.time.length == 1){ var values = { 'time': data.time, 'data': data[this.key] }; }else{ var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length); } var t = values.time.map(function(x) { return new Date(x*1000); }); for (var i = 0; i < data[this.key].length; i++){ this.n = this.n + 1; this.ybuffer.push([]); var trace = { x: t, y: data[this.key][i], mode: 'lines', type: 'scatter', name: 'Channel ' + i }; traces.push(trace); } var title = this.props.title || '' var layout = { // autosize: false, // width: 800, // height: 400, title: title + ' - ' + t.slice(-1).toLocaleString(), xaxis: { title: 'Time', linecolor: 'black', }, yaxis: { title: this.props.ylabel || 'dB', linecolor: 'black', }, titlefont: { size: 14, } }; if (this.props.ymin) {layout.yaxis.range = [this.props.ymin, this.props.ymax] } var conf = { modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'], displaylogo: false, showTips: true }; Plotly.newPlot('plot', traces, layout, conf); } getSize(){ var t = this.xbuffer.slice(-1)[0]; var n = 0; var timespan = (this.props.timespan || 12)*1000*60*60; while ((t-this.div.data[0].x[n]) > timespan){ n += 1; } return n; } fill_gaps(xBuffer, yBuffer, interval, N){ var x = [xBuffer[0]]; var y = []; for (var j = 0; j < N; j++){ y.push([yBuffer[j][0]]); } var last; for (var i = 1; i < xBuffer.length; i++){ var cnt = 0; last = x.slice(-1)[0]; while (Math.abs(parseFloat(xBuffer[i])-last-parseFloat(interval))>0.5){ cnt += 1; last = last+interval; x.push(last); for (var j = 0; j < N; j++){ z[j].push(null); } // Avoid infinite loop if (cnt==100){break;} } x.push(xBuffer[i]); for (var j = 0; j < N; j++){ y[j].push(yBuffer[j][i]); } } return {'time':x, 'data':y}; } plot(){ // add new data to plots and empty buffers var xvalues = []; var yvalues = []; var traces = []; var N = this.getSize(); console.log('Plotting...'); for (var i = 0; i < this.n; i++){ if (N > 0){ this.div.data[i].y = this.div.data[i].y.slice(N,) this.div.data[i].x = this.div.data[i].x.slice(N,) } yvalues.push(this.ybuffer[i]); xvalues.push(this.xbuffer); traces.push(i); this.ybuffer[i] = []; } Plotly.extendTraces(this.div, { y: yvalues, x: xvalues }, traces); this.xbuffer = []; } update(obj){ // fill data gaps var cnt = 0; while (Math.abs(parseFloat(obj.time[0])-this.last-parseFloat(obj.interval))>0.5){ cnt += 1; this.last += obj.interval; var newt = new Date((this.last)*1000); this.xbuffer.push(newt); for (var i = 0; i < this.n; i++){ this.ybuffer[i].push(null); } // Avoid infinite loop if (cnt==100){break;} } // update buffers this.last = parseFloat(obj.time[0]); var t = new Date(obj.time[0]*1000); this.xbuffer.push(t); for (var i = 0; i < this.n; i++){ this.ybuffer[i].push(obj[this.key][i][0]); } var title = this.props.title || '' Plotly.relayout(this.div, { title: title + ' - ' + t.toLocaleString(), }); // plot when ready (every 10 secs) if (!this.wait){ this.plot(); this.wait = true; } else { clearTimeout(this.lastFunc) this.lastFunc = setTimeout(function(scope) { if ((Date.now() - scope.lastRan) >= scope.timer) { scope.plot() scope.lastRan = Date.now() } }, this.timer - (Date.now() - this.lastRan), this) } } }